| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- // SPDX-License-Identifier: GPL-2.0-only
- // Copyright(c) 2024 Advanced Micro Devices, Inc.
- /*
- * acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
- */
- #include <linux/bitmap.h>
- #include <linux/device.h>
- #include <linux/dmi.h>
- #include <linux/module.h>
- #include <linux/soundwire/sdw.h>
- #include <linux/soundwire/sdw_type.h>
- #include <sound/soc.h>
- #include <sound/soc-acpi.h>
- #include "soc_amd_sdw_common.h"
- #include "../../codecs/rt711.h"
- static unsigned long sof_sdw_quirk = RT711_JD1;
- static int quirk_override = -1;
- module_param_named(quirk, quirk_override, int, 0444);
- MODULE_PARM_DESC(quirk, "Board-specific quirk override");
- static void log_quirks(struct device *dev)
- {
- if (SOC_JACK_JDSRC(sof_sdw_quirk))
- dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
- SOC_JACK_JDSRC(sof_sdw_quirk));
- if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
- dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
- }
- static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
- {
- sof_sdw_quirk = (unsigned long)id->driver_data;
- return 1;
- }
- static const struct dmi_system_id sof_sdw_quirk_table[] = {
- {
- .callback = sof_sdw_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
- },
- .driver_data = (void *)RT711_JD2,
- },
- {}
- };
- static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:04:00.5",
- }
- };
- static const struct snd_soc_ops sdw_ops = {
- .startup = asoc_sdw_startup,
- .prepare = asoc_sdw_prepare,
- .trigger = asoc_sdw_trigger,
- .hw_params = asoc_sdw_hw_params,
- .hw_free = asoc_sdw_hw_free,
- .shutdown = asoc_sdw_shutdown,
- };
- static int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
- {
- switch (sdw_link_id) {
- case AMD_SDW0:
- switch (be_id) {
- case SOC_SDW_JACK_OUT_DAI_ID:
- *cpu_pin_id = ACP63_SW0_AUDIO0_TX;
- break;
- case SOC_SDW_JACK_IN_DAI_ID:
- *cpu_pin_id = ACP63_SW0_AUDIO0_RX;
- break;
- case SOC_SDW_AMP_OUT_DAI_ID:
- *cpu_pin_id = ACP63_SW0_AUDIO1_TX;
- break;
- case SOC_SDW_AMP_IN_DAI_ID:
- *cpu_pin_id = ACP63_SW0_AUDIO1_RX;
- break;
- case SOC_SDW_DMIC_DAI_ID:
- *cpu_pin_id = ACP63_SW0_AUDIO2_RX;
- break;
- default:
- dev_err(dev, "Invalid be id:%d\n", be_id);
- return -EINVAL;
- }
- break;
- case AMD_SDW1:
- switch (be_id) {
- case SOC_SDW_JACK_OUT_DAI_ID:
- case SOC_SDW_AMP_OUT_DAI_ID:
- *cpu_pin_id = ACP63_SW1_AUDIO0_TX;
- break;
- case SOC_SDW_JACK_IN_DAI_ID:
- case SOC_SDW_AMP_IN_DAI_ID:
- case SOC_SDW_DMIC_DAI_ID:
- *cpu_pin_id = ACP63_SW1_AUDIO0_RX;
- break;
- default:
- dev_err(dev, "invalid be_id:%d\n", be_id);
- return -EINVAL;
- }
- break;
- default:
- dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
- return -EINVAL;
- }
- return 0;
- }
- static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
- static int create_sdw_dailink(struct snd_soc_card *card,
- struct asoc_sdw_dailink *sof_dai,
- struct snd_soc_dai_link **dai_links,
- int *be_id, struct snd_soc_codec_conf **codec_conf)
- {
- struct device *dev = card->dev;
- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
- struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
- struct asoc_sdw_endpoint *sof_end;
- int cpu_pin_id;
- int stream;
- int ret;
- list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
- if (sof_end->name_prefix) {
- (*codec_conf)->dlc.name = sof_end->codec_name;
- (*codec_conf)->name_prefix = sof_end->name_prefix;
- (*codec_conf)++;
- }
- if (sof_end->include_sidecar) {
- ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
- if (ret)
- return ret;
- }
- }
- for_each_pcm_streams(stream) {
- static const char * const sdw_stream_name[] = {
- "SDW%d-PIN%d-PLAYBACK",
- "SDW%d-PIN%d-CAPTURE",
- "SDW%d-PIN%d-PLAYBACK-%s",
- "SDW%d-PIN%d-CAPTURE-%s",
- };
- struct snd_soc_dai_link_ch_map *codec_maps;
- struct snd_soc_dai_link_component *codecs;
- struct snd_soc_dai_link_component *cpus;
- int num_cpus = hweight32(sof_dai->link_mask[stream]);
- int num_codecs = sof_dai->num_devs[stream];
- int playback, capture;
- int j = 0;
- char *name;
- if (!sof_dai->num_devs[stream])
- continue;
- sof_end = list_first_entry(&sof_dai->endpoints,
- struct asoc_sdw_endpoint, list);
- *be_id = sof_end->dai_info->dailink[stream];
- if (*be_id < 0) {
- dev_err(dev, "Invalid dailink id %d\n", *be_id);
- return -EINVAL;
- }
- switch (amd_ctx->acp_rev) {
- case ACP63_PCI_REV:
- ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
- *be_id, &cpu_pin_id, dev);
- if (ret)
- return ret;
- break;
- default:
- return -EINVAL;
- }
- /* create stream name according to first link id */
- if (ctx->append_dai_type) {
- name = devm_kasprintf(dev, GFP_KERNEL,
- sdw_stream_name[stream + 2],
- ffs(sof_end->link_mask) - 1,
- cpu_pin_id,
- type_strings[sof_end->dai_info->dai_type]);
- } else {
- name = devm_kasprintf(dev, GFP_KERNEL,
- sdw_stream_name[stream],
- ffs(sof_end->link_mask) - 1,
- cpu_pin_id);
- }
- if (!name)
- return -ENOMEM;
- cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
- if (!cpus)
- return -ENOMEM;
- codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
- if (!codecs)
- return -ENOMEM;
- codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
- if (!codec_maps)
- return -ENOMEM;
- list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
- if (!sof_end->dai_info->direction[stream])
- continue;
- int link_num = ffs(sof_end->link_mask) - 1;
- cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
- "SDW%d Pin%d",
- link_num, cpu_pin_id);
- dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
- if (!cpus->dai_name)
- return -ENOMEM;
- codec_maps[j].cpu = 0;
- codec_maps[j].codec = j;
- codecs[j].name = sof_end->codec_name;
- codecs[j].dai_name = sof_end->dai_info->dai_name;
- j++;
- }
- WARN_ON(j != num_codecs);
- playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
- capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
- asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
- cpus, num_cpus, platform_component,
- ARRAY_SIZE(platform_component), codecs, num_codecs,
- asoc_sdw_rtd_init, &sdw_ops);
- /*
- * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
- * based on wait_for_completion(), tag them as 'nonatomic'.
- */
- (*dai_links)->nonatomic = true;
- (*dai_links)->ch_maps = codec_maps;
- list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
- if (sof_end->dai_info->init)
- sof_end->dai_info->init(card, *dai_links,
- sof_end->codec_info,
- playback);
- }
- (*dai_links)++;
- }
- return 0;
- }
- static int create_sdw_dailinks(struct snd_soc_card *card,
- struct snd_soc_dai_link **dai_links, int *be_id,
- struct asoc_sdw_dailink *sof_dais,
- struct snd_soc_codec_conf **codec_conf)
- {
- int ret;
- /* generate DAI links by each sdw link */
- while (sof_dais->initialised) {
- int current_be_id;
- ret = create_sdw_dailink(card, sof_dais, dai_links,
- ¤t_be_id, codec_conf);
- if (ret)
- return ret;
- /* Update the be_id to match the highest ID used for SDW link */
- if (*be_id < current_be_id)
- *be_id = current_be_id;
- sof_dais++;
- }
- return 0;
- }
- static int create_dmic_dailinks(struct snd_soc_card *card,
- struct snd_soc_dai_link **dai_links, int *be_id)
- {
- struct device *dev = card->dev;
- int ret;
- ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
- 0, 1, // DMIC only supports capture
- "acp-sof-dmic", platform_component->name,
- ARRAY_SIZE(platform_component),
- "dmic-codec", "dmic-hifi",
- asoc_sdw_dmic_init, NULL);
- if (ret)
- return ret;
- (*dai_links)++;
- return 0;
- }
- static int sof_card_dai_links_create(struct snd_soc_card *card)
- {
- struct device *dev = card->dev;
- struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
- int sdw_be_num = 0, dmic_num = 0;
- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
- struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
- struct snd_soc_codec_conf *codec_conf;
- struct asoc_sdw_endpoint *sof_ends;
- struct asoc_sdw_dailink *sof_dais;
- struct snd_soc_dai_link *dai_links;
- int num_devs = 0;
- int num_ends = 0;
- int num_links;
- int be_id = 0;
- int ret;
- ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
- if (ret < 0) {
- dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
- return ret;
- }
- /* One per DAI link, worst case is a DAI link for every endpoint */
- sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
- if (!sof_dais)
- return -ENOMEM;
- /* One per endpoint, ie. each DAI on each codec/amp */
- sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
- if (!sof_ends) {
- ret = -ENOMEM;
- goto err_dai;
- }
- ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
- if (ret < 0)
- goto err_end;
- sdw_be_num = ret;
- /* enable dmic */
- if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
- dmic_num = 1;
- dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
- codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
- if (!codec_conf) {
- ret = -ENOMEM;
- goto err_end;
- }
- /* allocate BE dailinks */
- num_links = sdw_be_num + dmic_num;
- dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
- if (!dai_links) {
- ret = -ENOMEM;
- goto err_end;
- }
- card->codec_conf = codec_conf;
- card->num_configs = num_devs;
- card->dai_link = dai_links;
- card->num_links = num_links;
- /* SDW */
- if (sdw_be_num) {
- ret = create_sdw_dailinks(card, &dai_links, &be_id,
- sof_dais, &codec_conf);
- if (ret)
- goto err_end;
- }
- /* dmic */
- if (dmic_num > 0) {
- if (ctx->ignore_internal_dmic) {
- dev_warn(dev, "Ignoring ACP DMIC\n");
- } else {
- ret = create_dmic_dailinks(card, &dai_links, &be_id);
- if (ret)
- goto err_end;
- }
- }
- WARN_ON(codec_conf != card->codec_conf + card->num_configs);
- WARN_ON(dai_links != card->dai_link + card->num_links);
- err_end:
- kfree(sof_ends);
- err_dai:
- kfree(sof_dais);
- return ret;
- }
- static int mc_probe(struct platform_device *pdev)
- {
- struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
- struct snd_soc_card *card;
- struct amd_mc_ctx *amd_ctx;
- struct asoc_sdw_mc_private *ctx;
- int amp_num = 0, i;
- int ret;
- amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
- if (!amd_ctx)
- return -ENOMEM;
- amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
- amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
- ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
- ctx->private = amd_ctx;
- card = &ctx->card;
- card->dev = &pdev->dev;
- card->name = "amd-soundwire";
- card->owner = THIS_MODULE;
- card->late_probe = asoc_sdw_card_late_probe;
- snd_soc_card_set_drvdata(card, ctx);
- dmi_check_system(sof_sdw_quirk_table);
- if (quirk_override != -1) {
- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
- sof_sdw_quirk, quirk_override);
- sof_sdw_quirk = quirk_override;
- }
- log_quirks(card->dev);
- ctx->mc_quirk = sof_sdw_quirk;
- /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
- for (i = 0; i < ctx->codec_info_list_count; i++)
- codec_info_list[i].amp_num = 0;
- ret = sof_card_dai_links_create(card);
- if (ret < 0)
- return ret;
- /*
- * the default amp_num is zero for each codec and
- * amp_num will only be increased for active amp
- * codecs on used platform
- */
- for (i = 0; i < ctx->codec_info_list_count; i++)
- amp_num += codec_info_list[i].amp_num;
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- " cfg-amp:%d", amp_num);
- if (!card->components)
- return -ENOMEM;
- /* Register the card */
- ret = devm_snd_soc_register_card(card->dev, card);
- if (ret) {
- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
- asoc_sdw_mc_dailink_exit_loop(card);
- return ret;
- }
- platform_set_drvdata(pdev, card);
- return ret;
- }
- static void mc_remove(struct platform_device *pdev)
- {
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- asoc_sdw_mc_dailink_exit_loop(card);
- }
- static const struct platform_device_id mc_id_table[] = {
- { "amd_sof_sdw", },
- {}
- };
- MODULE_DEVICE_TABLE(platform, mc_id_table);
- static struct platform_driver sof_sdw_driver = {
- .driver = {
- .name = "amd_sof_sdw",
- .pm = &snd_soc_pm_ops,
- },
- .probe = mc_probe,
- .remove = mc_remove,
- .id_table = mc_id_table,
- };
- module_platform_driver(sof_sdw_driver);
- MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
- MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
- MODULE_LICENSE("GPL");
- MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);
|