505 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright(c) 2024 Advanced Micro Devices, Inc.3 4/*5 * acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms6 */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 sof_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(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 33static 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 39static 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 51static 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 58static 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 67static 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 115static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};116 117static 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 operations243 * 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 261static 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 287static 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 capture295 "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 307static 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 395err_end:396 kfree(sof_ends);397err_dai:398 kfree(sof_dais);399 400 return ret;401}402 403static 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 and452 * amp_num will only be increased for active amp453 * codecs on used platform454 */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 476static 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 483static const struct platform_device_id mc_id_table[] = {484 { "amd_sof_sdw", },485 {}486};487MODULE_DEVICE_TABLE(platform, mc_id_table);488 489static 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 499module_platform_driver(sof_sdw_driver);500 501MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");502MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");503MODULE_LICENSE("GPL");504MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);505