1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright(c) 2024 Advanced Micro Devices, Inc. 3 4 /* 5 * acp-sdw-sof-mach - ASoC 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 19 static unsigned long sof_sdw_quirk = RT711_JD1; 20 static int quirk_override = -1; 21 module_param_named(quirk, quirk_override, int, 0444); 22 MODULE_PARM_DESC(quirk, "Board-specific quirk override"); 23 24 static void log_quirks(struct device *dev) 25 { 26 if (SOC_JACK_JDSRC(sof_sdw_quirk)) 27 dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", 28 SOC_JACK_JDSRC(sof_sdw_quirk)); 29 if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC) 30 dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n"); 31 } 32 33 static int sof_sdw_quirk_cb(const struct dmi_system_id *id) 34 { 35 sof_sdw_quirk = (unsigned long)id->driver_data; 36 return 1; 37 } 38 39 static const struct dmi_system_id sof_sdw_quirk_table[] = { 40 { 41 .callback = sof_sdw_quirk_cb, 42 .matches = { 43 DMI_MATCH(DMI_SYS_VENDOR, "AMD"), 44 DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"), 45 }, 46 .driver_data = (void *)RT711_JD2, 47 }, 48 {} 49 }; 50 51 static struct snd_soc_dai_link_component platform_component[] = { 52 { 53 /* name might be overridden during probe */ 54 .name = "0000:04:00.5", 55 } 56 }; 57 58 static const struct snd_soc_ops sdw_ops = { 59 .startup = asoc_sdw_startup, 60 .prepare = asoc_sdw_prepare, 61 .trigger = asoc_sdw_trigger, 62 .hw_params = asoc_sdw_hw_params, 63 .hw_free = asoc_sdw_hw_free, 64 .shutdown = asoc_sdw_shutdown, 65 }; 66 67 static int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev) 68 { 69 switch (sdw_link_id) { 70 case AMD_SDW0: 71 switch (be_id) { 72 case SOC_SDW_JACK_OUT_DAI_ID: 73 *cpu_pin_id = ACP63_SW0_AUDIO0_TX; 74 break; 75 case SOC_SDW_JACK_IN_DAI_ID: 76 *cpu_pin_id = ACP63_SW0_AUDIO0_RX; 77 break; 78 case SOC_SDW_AMP_OUT_DAI_ID: 79 *cpu_pin_id = ACP63_SW0_AUDIO1_TX; 80 break; 81 case SOC_SDW_AMP_IN_DAI_ID: 82 *cpu_pin_id = ACP63_SW0_AUDIO1_RX; 83 break; 84 case SOC_SDW_DMIC_DAI_ID: 85 *cpu_pin_id = ACP63_SW0_AUDIO2_RX; 86 break; 87 default: 88 dev_err(dev, "Invalid be id:%d\n", be_id); 89 return -EINVAL; 90 } 91 break; 92 case AMD_SDW1: 93 switch (be_id) { 94 case SOC_SDW_JACK_OUT_DAI_ID: 95 case SOC_SDW_AMP_OUT_DAI_ID: 96 *cpu_pin_id = ACP63_SW1_AUDIO0_TX; 97 break; 98 case SOC_SDW_JACK_IN_DAI_ID: 99 case SOC_SDW_AMP_IN_DAI_ID: 100 case SOC_SDW_DMIC_DAI_ID: 101 *cpu_pin_id = ACP63_SW1_AUDIO0_RX; 102 break; 103 default: 104 dev_err(dev, "invalid be_id:%d\n", be_id); 105 return -EINVAL; 106 } 107 break; 108 default: 109 dev_err(dev, "Invalid link id:%d\n", sdw_link_id); 110 return -EINVAL; 111 } 112 return 0; 113 } 114 115 static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; 116 117 static int create_sdw_dailink(struct snd_soc_card *card, 118 struct asoc_sdw_dailink *sof_dai, 119 struct snd_soc_dai_link **dai_links, 120 int *be_id, struct snd_soc_codec_conf **codec_conf) 121 { 122 struct device *dev = card->dev; 123 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 124 struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private; 125 struct asoc_sdw_endpoint *sof_end; 126 int cpu_pin_id; 127 int stream; 128 int ret; 129 130 list_for_each_entry(sof_end, &sof_dai->endpoints, list) { 131 if (sof_end->name_prefix) { 132 (*codec_conf)->dlc.name = sof_end->codec_name; 133 (*codec_conf)->name_prefix = sof_end->name_prefix; 134 (*codec_conf)++; 135 } 136 137 if (sof_end->include_sidecar) { 138 ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf); 139 if (ret) 140 return ret; 141 } 142 } 143 144 for_each_pcm_streams(stream) { 145 static const char * const sdw_stream_name[] = { 146 "SDW%d-PIN%d-PLAYBACK", 147 "SDW%d-PIN%d-CAPTURE", 148 "SDW%d-PIN%d-PLAYBACK-%s", 149 "SDW%d-PIN%d-CAPTURE-%s", 150 }; 151 struct snd_soc_dai_link_ch_map *codec_maps; 152 struct snd_soc_dai_link_component *codecs; 153 struct snd_soc_dai_link_component *cpus; 154 int num_cpus = hweight32(sof_dai->link_mask[stream]); 155 int num_codecs = sof_dai->num_devs[stream]; 156 int playback, capture; 157 int i = 0, j = 0; 158 char *name; 159 160 if (!sof_dai->num_devs[stream]) 161 continue; 162 163 sof_end = list_first_entry(&sof_dai->endpoints, 164 struct asoc_sdw_endpoint, list); 165 166 *be_id = sof_end->dai_info->dailink[stream]; 167 if (*be_id < 0) { 168 dev_err(dev, "Invalid dailink id %d\n", *be_id); 169 return -EINVAL; 170 } 171 172 switch (amd_ctx->acp_rev) { 173 case ACP63_PCI_REV: 174 ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1), 175 *be_id, &cpu_pin_id, dev); 176 if (ret) 177 return ret; 178 break; 179 default: 180 return -EINVAL; 181 } 182 /* create stream name according to first link id */ 183 if (ctx->append_dai_type) { 184 name = devm_kasprintf(dev, GFP_KERNEL, 185 sdw_stream_name[stream + 2], 186 ffs(sof_end->link_mask) - 1, 187 cpu_pin_id, 188 type_strings[sof_end->dai_info->dai_type]); 189 } else { 190 name = devm_kasprintf(dev, GFP_KERNEL, 191 sdw_stream_name[stream], 192 ffs(sof_end->link_mask) - 1, 193 cpu_pin_id); 194 } 195 if (!name) 196 return -ENOMEM; 197 198 cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL); 199 if (!cpus) 200 return -ENOMEM; 201 202 codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL); 203 if (!codecs) 204 return -ENOMEM; 205 206 codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL); 207 if (!codec_maps) 208 return -ENOMEM; 209 210 list_for_each_entry(sof_end, &sof_dai->endpoints, list) { 211 if (!sof_end->dai_info->direction[stream]) 212 continue; 213 214 int link_num = ffs(sof_end->link_mask) - 1; 215 216 cpus[i].dai_name = devm_kasprintf(dev, GFP_KERNEL, 217 "SDW%d Pin%d", 218 link_num, cpu_pin_id); 219 dev_dbg(dev, "cpu[%d].dai_name:%s\n", i, cpus[i].dai_name); 220 if (!cpus[i].dai_name) 221 return -ENOMEM; 222 223 codec_maps[j].cpu = i; 224 codec_maps[j].codec = j; 225 226 codecs[j].name = sof_end->codec_name; 227 codecs[j].dai_name = sof_end->dai_info->dai_name; 228 j++; 229 } 230 231 WARN_ON(j != num_codecs); 232 233 playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); 234 capture = (stream == SNDRV_PCM_STREAM_CAPTURE); 235 236 asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture, 237 cpus, num_cpus, platform_component, 238 ARRAY_SIZE(platform_component), codecs, num_codecs, 239 asoc_sdw_rtd_init, &sdw_ops); 240 241 /* 242 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations 243 * based on wait_for_completion(), tag them as 'nonatomic'. 244 */ 245 (*dai_links)->nonatomic = true; 246 (*dai_links)->ch_maps = codec_maps; 247 248 list_for_each_entry(sof_end, &sof_dai->endpoints, list) { 249 if (sof_end->dai_info->init) 250 sof_end->dai_info->init(card, *dai_links, 251 sof_end->codec_info, 252 playback); 253 } 254 255 (*dai_links)++; 256 } 257 258 return 0; 259 } 260 261 static int create_sdw_dailinks(struct snd_soc_card *card, 262 struct snd_soc_dai_link **dai_links, int *be_id, 263 struct asoc_sdw_dailink *sof_dais, 264 struct snd_soc_codec_conf **codec_conf) 265 { 266 int ret; 267 268 /* generate DAI links by each sdw link */ 269 while (sof_dais->initialised) { 270 int current_be_id; 271 272 ret = create_sdw_dailink(card, sof_dais, dai_links, 273 ¤t_be_id, codec_conf); 274 if (ret) 275 return ret; 276 277 /* Update the be_id to match the highest ID used for SDW link */ 278 if (*be_id < current_be_id) 279 *be_id = current_be_id; 280 281 sof_dais++; 282 } 283 284 return 0; 285 } 286 287 static int create_dmic_dailinks(struct snd_soc_card *card, 288 struct snd_soc_dai_link **dai_links, int *be_id) 289 { 290 struct device *dev = card->dev; 291 int ret; 292 293 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec", 294 0, 1, // DMIC only supports capture 295 "acp-sof-dmic", platform_component->name, 296 ARRAY_SIZE(platform_component), 297 "dmic-codec", "dmic-hifi", 298 asoc_sdw_dmic_init, NULL); 299 if (ret) 300 return ret; 301 302 (*dai_links)++; 303 304 return 0; 305 } 306 307 static int sof_card_dai_links_create(struct snd_soc_card *card) 308 { 309 struct device *dev = card->dev; 310 struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 311 int sdw_be_num = 0, dmic_num = 0; 312 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 313 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 314 struct snd_soc_codec_conf *codec_conf; 315 struct asoc_sdw_endpoint *sof_ends; 316 struct asoc_sdw_dailink *sof_dais; 317 struct snd_soc_dai_link *dai_links; 318 int num_devs = 0; 319 int num_ends = 0; 320 int num_links; 321 int be_id = 0; 322 int ret; 323 324 ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends); 325 if (ret < 0) { 326 dev_err(dev, "failed to count devices/endpoints: %d\n", ret); 327 return ret; 328 } 329 330 /* One per DAI link, worst case is a DAI link for every endpoint */ 331 sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL); 332 if (!sof_dais) 333 return -ENOMEM; 334 335 /* One per endpoint, ie. each DAI on each codec/amp */ 336 sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL); 337 if (!sof_ends) { 338 ret = -ENOMEM; 339 goto err_dai; 340 } 341 342 ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs); 343 if (ret < 0) 344 goto err_end; 345 346 sdw_be_num = ret; 347 348 /* enable dmic */ 349 if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num) 350 dmic_num = 1; 351 352 dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); 353 354 codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL); 355 if (!codec_conf) { 356 ret = -ENOMEM; 357 goto err_end; 358 } 359 360 /* allocate BE dailinks */ 361 num_links = sdw_be_num + dmic_num; 362 dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); 363 if (!dai_links) { 364 ret = -ENOMEM; 365 goto err_end; 366 } 367 368 card->codec_conf = codec_conf; 369 card->num_configs = num_devs; 370 card->dai_link = dai_links; 371 card->num_links = num_links; 372 373 /* SDW */ 374 if (sdw_be_num) { 375 ret = create_sdw_dailinks(card, &dai_links, &be_id, 376 sof_dais, &codec_conf); 377 if (ret) 378 goto err_end; 379 } 380 381 /* dmic */ 382 if (dmic_num > 0) { 383 if (ctx->ignore_internal_dmic) { 384 dev_warn(dev, "Ignoring ACP DMIC\n"); 385 } else { 386 ret = create_dmic_dailinks(card, &dai_links, &be_id); 387 if (ret) 388 goto err_end; 389 } 390 } 391 392 WARN_ON(codec_conf != card->codec_conf + card->num_configs); 393 WARN_ON(dai_links != card->dai_link + card->num_links); 394 395 err_end: 396 kfree(sof_ends); 397 err_dai: 398 kfree(sof_dais); 399 400 return ret; 401 } 402 403 static int mc_probe(struct platform_device *pdev) 404 { 405 struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); 406 struct snd_soc_card *card; 407 struct amd_mc_ctx *amd_ctx; 408 struct asoc_sdw_mc_private *ctx; 409 int amp_num = 0, i; 410 int ret; 411 412 amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL); 413 if (!amd_ctx) 414 return -ENOMEM; 415 416 amd_ctx->acp_rev = mach->mach_params.subsystem_rev; 417 amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS; 418 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 419 if (!ctx) 420 return -ENOMEM; 421 ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count(); 422 ctx->private = amd_ctx; 423 card = &ctx->card; 424 card->dev = &pdev->dev; 425 card->name = "amd-soundwire"; 426 card->owner = THIS_MODULE; 427 card->late_probe = asoc_sdw_card_late_probe; 428 429 snd_soc_card_set_drvdata(card, ctx); 430 431 dmi_check_system(sof_sdw_quirk_table); 432 433 if (quirk_override != -1) { 434 dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", 435 sof_sdw_quirk, quirk_override); 436 sof_sdw_quirk = quirk_override; 437 } 438 439 log_quirks(card->dev); 440 441 ctx->mc_quirk = sof_sdw_quirk; 442 /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ 443 for (i = 0; i < ctx->codec_info_list_count; i++) 444 codec_info_list[i].amp_num = 0; 445 446 ret = sof_card_dai_links_create(card); 447 if (ret < 0) 448 return ret; 449 450 /* 451 * the default amp_num is zero for each codec and 452 * amp_num will only be increased for active amp 453 * codecs on used platform 454 */ 455 for (i = 0; i < ctx->codec_info_list_count; i++) 456 amp_num += codec_info_list[i].amp_num; 457 458 card->components = devm_kasprintf(card->dev, GFP_KERNEL, 459 " cfg-amp:%d", amp_num); 460 if (!card->components) 461 return -ENOMEM; 462 463 /* Register the card */ 464 ret = devm_snd_soc_register_card(card->dev, card); 465 if (ret) { 466 dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); 467 asoc_sdw_mc_dailink_exit_loop(card); 468 return ret; 469 } 470 471 platform_set_drvdata(pdev, card); 472 473 return ret; 474 } 475 476 static void mc_remove(struct platform_device *pdev) 477 { 478 struct snd_soc_card *card = platform_get_drvdata(pdev); 479 480 asoc_sdw_mc_dailink_exit_loop(card); 481 } 482 483 static const struct platform_device_id mc_id_table[] = { 484 { "amd_sof_sdw", }, 485 {} 486 }; 487 MODULE_DEVICE_TABLE(platform, mc_id_table); 488 489 static struct platform_driver sof_sdw_driver = { 490 .driver = { 491 .name = "amd_sof_sdw", 492 .pm = &snd_soc_pm_ops, 493 }, 494 .probe = mc_probe, 495 .remove = mc_remove, 496 .id_table = mc_id_table, 497 }; 498 499 module_platform_driver(sof_sdw_driver); 500 501 MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver"); 502 MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com"); 503 MODULE_LICENSE("GPL"); 504 MODULE_IMPORT_NS(SND_SOC_SDW_UTILS); 505
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.