FILE
acp-sdw-legacy-mach.c
File preview with syntax highlighting.
linux-6.19.14/sound/soc/amd/acp/acp-sdw-legacy-mach.c
1// SPDX-License-Identifier: GPL-2.0-only 2// Copyright(c) 2024 Advanced Micro Devices, Inc. 3 4/* 5 * acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms 6 */ 7 8#include <linux/bitmap.h> 9#include <linux/device.h> 10#include <linux/dmi.h> 11#include <linux/module.h> 12#include <linux/soundwire/sdw.h> 13#include <linux/soundwire/sdw_type.h> 14#include <sound/soc.h> 15#include <sound/soc-acpi.h> 16#include "soc_amd_sdw_common.h" 17#include "../../codecs/rt711.h" 18 19static unsigned long soc_sdw_quirk = RT711_JD1; 20static int quirk_override = -1; 21module_param_named(quirk, quirk_override, int, 0444); 22MODULE_PARM_DESC(quirk, "Board-specific quirk override"); 23 24static void log_quirks(struct device *dev) 25{ 26 if (SOC_JACK_JDSRC(soc_sdw_quirk)) 27 dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", 28 SOC_JACK_JDSRC(soc_sdw_quirk)); 29 if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) 30 dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n"); 31 if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR) 32 dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n"); 33} 34 35static int soc_sdw_quirk_cb(const struct dmi_system_id *id) 36{ 37 soc_sdw_quirk = (unsigned long)id->driver_data; 38 return 1; 39} 40 41static const struct dmi_system_id soc_sdw_quirk_table[] = { 42 { 43 .callback = soc_sdw_quirk_cb, 44 .matches = { 45 DMI_MATCH(DMI_SYS_VENDOR, "AMD"), 46 DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"), 47 }, 48 .driver_data = (void *)RT711_JD2, 49 }, 50 { 51 .callback = soc_sdw_quirk_cb, 52 .matches = { 53 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 54 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"), 55 }, 56 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 57 }, 58 { 59 .callback = soc_sdw_quirk_cb, 60 .matches = { 61 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 62 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"), 63 }, 64 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 65 }, 66 { 67 .callback = soc_sdw_quirk_cb, 68 .matches = { 69 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 70 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"), 71 }, 72 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 73 }, 74 { 75 .callback = soc_sdw_quirk_cb, 76 .matches = { 77 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 78 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"), 79 }, 80 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 81 }, 82 { 83 .callback = soc_sdw_quirk_cb, 84 .matches = { 85 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 86 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD3"), 87 }, 88 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 89 }, 90 { 91 .callback = soc_sdw_quirk_cb, 92 .matches = { 93 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 94 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD4"), 95 }, 96 .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), 97 }, 98 { 99 .callback = soc_sdw_quirk_cb, 100 .matches = { 101 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 102 DMI_MATCH(DMI_PRODUCT_SKU, "21YW"), 103 }, 104 .driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)), 105 }, 106 { 107 .callback = soc_sdw_quirk_cb, 108 .matches = { 109 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 110 DMI_MATCH(DMI_PRODUCT_SKU, "21YX"), 111 }, 112 .driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)), 113 }, 114 { 115 .callback = soc_sdw_quirk_cb, 116 .matches = { 117 DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), 118 DMI_MATCH(DMI_PRODUCT_NAME, "HN7306EA"), 119 }, 120 .driver_data = (void *)(ASOC_SDW_ACP_DMIC), 121 }, 122 {} 123}; 124 125static const struct snd_soc_ops sdw_ops = { 126 .startup = asoc_sdw_startup, 127 .prepare = asoc_sdw_prepare, 128 .trigger = asoc_sdw_trigger, 129 .hw_params = asoc_sdw_hw_params, 130 .hw_free = asoc_sdw_hw_free, 131 .shutdown = asoc_sdw_shutdown, 132}; 133 134static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; 135 136static int create_sdw_dailink(struct snd_soc_card *card, 137 struct asoc_sdw_dailink *soc_dai, 138 struct snd_soc_dai_link **dai_links, 139 int *be_id, struct snd_soc_codec_conf **codec_conf, 140 struct snd_soc_dai_link_component *sdw_platform_component) 141{ 142 struct device *dev = card->dev; 143 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 144 struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; 145 struct asoc_sdw_endpoint *soc_end; 146 int cpu_pin_id; 147 int stream; 148 int ret; 149 150 list_for_each_entry(soc_end, &soc_dai->endpoints, list) { 151 if (soc_end->name_prefix) { 152 (*codec_conf)->dlc.name = soc_end->codec_name; 153 (*codec_conf)->name_prefix = soc_end->name_prefix; 154 (*codec_conf)++; 155 } 156 157 if (soc_end->include_sidecar) { 158 ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf); 159 if (ret) 160 return ret; 161 } 162 } 163 164 for_each_pcm_streams(stream) { 165 static const char * const sdw_stream_name[] = { 166 "SDW%d-PIN%d-PLAYBACK", 167 "SDW%d-PIN%d-CAPTURE", 168 "SDW%d-PIN%d-PLAYBACK-%s", 169 "SDW%d-PIN%d-CAPTURE-%s", 170 }; 171 struct snd_soc_dai_link_ch_map *codec_maps; 172 struct snd_soc_dai_link_component *codecs; 173 struct snd_soc_dai_link_component *cpus; 174 int num_cpus = hweight32(soc_dai->link_mask[stream]); 175 int num_codecs = soc_dai->num_devs[stream]; 176 int playback, capture; 177 int j = 0; 178 char *name; 179 180 if (!soc_dai->num_devs[stream]) 181 continue; 182 183 soc_end = list_first_entry(&soc_dai->endpoints, 184 struct asoc_sdw_endpoint, list); 185 186 *be_id = soc_end->dai_info->dailink[stream]; 187 if (*be_id < 0) { 188 dev_err(dev, "Invalid dailink id %d\n", *be_id); 189 return -EINVAL; 190 } 191 192 switch (amd_ctx->acp_rev) { 193 case ACP63_PCI_REV: 194 ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1), 195 *be_id, &cpu_pin_id, dev); 196 if (ret) 197 return ret; 198 break; 199 case ACP70_PCI_REV: 200 case ACP71_PCI_REV: 201 case ACP72_PCI_REV: 202 ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1), 203 *be_id, &cpu_pin_id, dev); 204 if (ret) 205 return ret; 206 break; 207 default: 208 return -EINVAL; 209 } 210 /* create stream name according to first link id */ 211 if (ctx->append_dai_type) { 212 name = devm_kasprintf(dev, GFP_KERNEL, 213 sdw_stream_name[stream + 2], 214 ffs(soc_end->link_mask) - 1, 215 cpu_pin_id, 216 type_strings[soc_end->dai_info->dai_type]); 217 } else { 218 name = devm_kasprintf(dev, GFP_KERNEL, 219 sdw_stream_name[stream], 220 ffs(soc_end->link_mask) - 1, 221 cpu_pin_id); 222 } 223 if (!name) 224 return -ENOMEM; 225 226 cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL); 227 if (!cpus) 228 return -ENOMEM; 229 230 codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL); 231 if (!codecs) 232 return -ENOMEM; 233 234 codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL); 235 if (!codec_maps) 236 return -ENOMEM; 237 238 list_for_each_entry(soc_end, &soc_dai->endpoints, list) { 239 if (!soc_end->dai_info->direction[stream]) 240 continue; 241 242 int link_num = ffs(soc_end->link_mask) - 1; 243 244 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, 245 "SDW%d Pin%d", 246 link_num, cpu_pin_id); 247 dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); 248 if (!cpus->dai_name) 249 return -ENOMEM; 250 251 codec_maps[j].cpu = 0; 252 codec_maps[j].codec = j; 253 254 codecs[j].name = soc_end->codec_name; 255 codecs[j].dai_name = soc_end->dai_info->dai_name; 256 j++; 257 } 258 259 WARN_ON(j != num_codecs); 260 261 playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); 262 capture = (stream == SNDRV_PCM_STREAM_CAPTURE); 263 264 asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture, 265 cpus, num_cpus, sdw_platform_component, 266 1, codecs, num_codecs, 267 0, asoc_sdw_rtd_init, &sdw_ops); 268 /* 269 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations 270 * based on wait_for_completion(), tag them as 'nonatomic'. 271 */ 272 (*dai_links)->nonatomic = true; 273 (*dai_links)->ch_maps = codec_maps; 274 275 list_for_each_entry(soc_end, &soc_dai->endpoints, list) { 276 if (soc_end->dai_info->init) 277 soc_end->dai_info->init(card, *dai_links, 278 soc_end->codec_info, 279 playback); 280 } 281 282 (*dai_links)++; 283 } 284 285 return 0; 286} 287 288static int create_sdw_dailinks(struct snd_soc_card *card, 289 struct snd_soc_dai_link **dai_links, int *be_id, 290 struct asoc_sdw_dailink *soc_dais, 291 struct snd_soc_codec_conf **codec_conf) 292{ 293 struct device *dev = card->dev; 294 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 295 struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; 296 struct snd_soc_dai_link_component *sdw_platform_component; 297 int ret; 298 299 sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 300 GFP_KERNEL); 301 if (!sdw_platform_component) 302 return -ENOMEM; 303 304 switch (amd_ctx->acp_rev) { 305 case ACP63_PCI_REV: 306 case ACP70_PCI_REV: 307 case ACP71_PCI_REV: 308 case ACP72_PCI_REV: 309 sdw_platform_component->name = "amd_ps_sdw_dma.0"; 310 break; 311 default: 312 return -EINVAL; 313 } 314 315 /* generate DAI links by each sdw link */ 316 while (soc_dais->initialised) { 317 int current_be_id = 0; 318 319 ret = create_sdw_dailink(card, soc_dais, dai_links, 320 ¤t_be_id, codec_conf, sdw_platform_component); 321 if (ret) 322 return ret; 323 324 /* Update the be_id to match the highest ID used for SDW link */ 325 if (*be_id < current_be_id) 326 *be_id = current_be_id; 327 328 soc_dais++; 329 } 330 331 return 0; 332} 333 334static int create_dmic_dailinks(struct snd_soc_card *card, 335 struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm) 336{ 337 struct device *dev = card->dev; 338 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 339 struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; 340 struct snd_soc_dai_link_component *pdm_cpu; 341 struct snd_soc_dai_link_component *pdm_platform; 342 int ret; 343 344 pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); 345 if (!pdm_cpu) 346 return -ENOMEM; 347 348 pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); 349 if (!pdm_platform) 350 return -ENOMEM; 351 352 switch (amd_ctx->acp_rev) { 353 case ACP63_PCI_REV: 354 case ACP70_PCI_REV: 355 case ACP71_PCI_REV: 356 case ACP72_PCI_REV: 357 pdm_cpu->name = "acp_ps_pdm_dma.0"; 358 pdm_platform->name = "acp_ps_pdm_dma.0"; 359 break; 360 default: 361 return -EINVAL; 362 } 363 364 *be_id = ACP_DMIC_BE_ID; 365 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec", 366 0, 1, // DMIC only supports capture 367 pdm_cpu->name, pdm_platform->name, 368 "dmic-codec.0", "dmic-hifi", no_pcm, 369 asoc_sdw_dmic_init, NULL); 370 if (ret) 371 return ret; 372 373 (*dai_links)++; 374 375 return 0; 376} 377 378static int soc_card_dai_links_create(struct snd_soc_card *card) 379{ 380 struct device *dev = card->dev; 381 struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 382 int sdw_be_num = 0, dmic_num = 0; 383 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 384 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 385 struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL; 386 struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL; 387 struct snd_soc_aux_dev *soc_aux; 388 struct snd_soc_codec_conf *codec_conf; 389 struct snd_soc_dai_link *dai_links; 390 int num_devs = 0; 391 int num_ends = 0; 392 int num_aux = 0; 393 int num_confs; 394 int num_links; 395 int be_id = 0; 396 int ret; 397 398 ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux); 399 if (ret < 0) { 400 dev_err(dev, "failed to count devices/endpoints: %d\n", ret); 401 return ret; 402 } 403 404 num_confs = num_ends; 405 406 /* One per DAI link, worst case is a DAI link for every endpoint */ 407 soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL); 408 if (!soc_dais) 409 return -ENOMEM; 410 411 /* One per endpoint, ie. each DAI on each codec/amp */ 412 soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL); 413 if (!soc_ends) 414 return -ENOMEM; 415 416 soc_aux = devm_kcalloc(dev, num_aux, sizeof(*soc_aux), GFP_KERNEL); 417 if (!soc_aux) 418 return -ENOMEM; 419 420 ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); 421 if (ret < 0) 422 return ret; 423 424 sdw_be_num = ret; 425 426 /* enable dmic */ 427 if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num) 428 dmic_num = 1; 429 430 dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); 431 432 codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL); 433 if (!codec_conf) 434 return -ENOMEM; 435 436 /* allocate BE dailinks */ 437 num_links = sdw_be_num + dmic_num; 438 dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); 439 if (!dai_links) 440 return -ENOMEM; 441 442 card->codec_conf = codec_conf; 443 card->num_configs = num_confs; 444 card->dai_link = dai_links; 445 card->num_links = num_links; 446 card->aux_dev = soc_aux; 447 card->num_aux_devs = num_aux; 448 449 /* SDW */ 450 if (sdw_be_num) { 451 ret = create_sdw_dailinks(card, &dai_links, &be_id, 452 soc_dais, &codec_conf); 453 if (ret) 454 return ret; 455 } 456 457 /* dmic */ 458 if (dmic_num > 0) { 459 if (ctx->ignore_internal_dmic) { 460 dev_warn(dev, "Ignoring ACP DMIC\n"); 461 } else { 462 ret = create_dmic_dailinks(card, &dai_links, &be_id, 0); 463 if (ret) 464 return ret; 465 } 466 } 467 468 WARN_ON(codec_conf != card->codec_conf + card->num_configs); 469 WARN_ON(dai_links != card->dai_link + card->num_links); 470 471 return ret; 472} 473 474static int mc_probe(struct platform_device *pdev) 475{ 476 struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); 477 struct snd_soc_card *card; 478 struct amd_mc_ctx *amd_ctx; 479 struct asoc_sdw_mc_private *ctx; 480 int amp_num = 0, i; 481 int ret; 482 483 amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL); 484 if (!amd_ctx) 485 return -ENOMEM; 486 487 amd_ctx->acp_rev = mach->mach_params.subsystem_rev; 488 amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS; 489 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 490 if (!ctx) 491 return -ENOMEM; 492 ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count(); 493 ctx->private = amd_ctx; 494 card = &ctx->card; 495 card->dev = &pdev->dev; 496 card->name = "amd-soundwire"; 497 card->owner = THIS_MODULE; 498 card->late_probe = asoc_sdw_card_late_probe; 499 500 snd_soc_card_set_drvdata(card, ctx); 501 if (mach->mach_params.subsystem_id_set) 502 snd_soc_card_set_pci_ssid(card, 503 mach->mach_params.subsystem_vendor, 504 mach->mach_params.subsystem_device); 505 506 dmi_check_system(soc_sdw_quirk_table); 507 508 if (quirk_override != -1) { 509 dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", 510 soc_sdw_quirk, quirk_override); 511 soc_sdw_quirk = quirk_override; 512 } 513 514 log_quirks(card->dev); 515 516 ctx->mc_quirk = soc_sdw_quirk; 517 dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); 518 /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ 519 for (i = 0; i < ctx->codec_info_list_count; i++) 520 codec_info_list[i].amp_num = 0; 521 522 ret = soc_card_dai_links_create(card); 523 if (ret < 0) 524 return ret; 525 526 /* 527 * the default amp_num is zero for each codec and 528 * amp_num will only be increased for active amp 529 * codecs on used platform 530 */ 531 for (i = 0; i < ctx->codec_info_list_count; i++) 532 amp_num += codec_info_list[i].amp_num; 533 534 card->components = devm_kasprintf(card->dev, GFP_KERNEL, 535 " cfg-amp:%d", amp_num); 536 if (!card->components) 537 return -ENOMEM; 538 if (mach->mach_params.dmic_num) { 539 card->components = devm_kasprintf(card->dev, GFP_KERNEL, 540 "%s mic:dmic cfg-mics:%d", 541 card->components, 542 mach->mach_params.dmic_num); 543 if (!card->components) 544 return -ENOMEM; 545 } 546 547 /* Register the card */ 548 ret = devm_snd_soc_register_card(card->dev, card); 549 if (ret) { 550 dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); 551 asoc_sdw_mc_dailink_exit_loop(card); 552 return ret; 553 } 554 555 platform_set_drvdata(pdev, card); 556 557 return ret; 558} 559 560static void mc_remove(struct platform_device *pdev) 561{ 562 struct snd_soc_card *card = platform_get_drvdata(pdev); 563 564 asoc_sdw_mc_dailink_exit_loop(card); 565} 566 567static const struct platform_device_id mc_id_table[] = { 568 { "amd_sdw", }, 569 {} 570}; 571MODULE_DEVICE_TABLE(platform, mc_id_table); 572 573static struct platform_driver soc_sdw_driver = { 574 .driver = { 575 .name = "amd_sdw", 576 .pm = &snd_soc_pm_ops, 577 }, 578 .probe = mc_probe, 579 .remove = mc_remove, 580 .id_table = mc_id_table, 581}; 582 583module_platform_driver(soc_sdw_driver); 584 585MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver"); 586MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>"); 587MODULE_LICENSE("GPL"); 588MODULE_IMPORT_NS("SND_SOC_SDW_UTILS"); 589MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
~/
Read-only file preview.