Path: blob/master/sound/soc/amd/acp/acp-sdw-legacy-mach.c
29268 views
// SPDX-License-Identifier: GPL-2.0-only1// Copyright(c) 2024 Advanced Micro Devices, Inc.23/*4* acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms5*/67#include <linux/bitmap.h>8#include <linux/device.h>9#include <linux/dmi.h>10#include <linux/module.h>11#include <linux/soundwire/sdw.h>12#include <linux/soundwire/sdw_type.h>13#include <sound/soc.h>14#include <sound/soc-acpi.h>15#include "soc_amd_sdw_common.h"16#include "../../codecs/rt711.h"1718static unsigned long soc_sdw_quirk = RT711_JD1;19static int quirk_override = -1;20module_param_named(quirk, quirk_override, int, 0444);21MODULE_PARM_DESC(quirk, "Board-specific quirk override");2223static void log_quirks(struct device *dev)24{25if (SOC_JACK_JDSRC(soc_sdw_quirk))26dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",27SOC_JACK_JDSRC(soc_sdw_quirk));28if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)29dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");30if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR)31dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n");32}3334static int soc_sdw_quirk_cb(const struct dmi_system_id *id)35{36soc_sdw_quirk = (unsigned long)id->driver_data;37return 1;38}3940static const struct dmi_system_id soc_sdw_quirk_table[] = {41{42.callback = soc_sdw_quirk_cb,43.matches = {44DMI_MATCH(DMI_SYS_VENDOR, "AMD"),45DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),46},47.driver_data = (void *)RT711_JD2,48},49{50.callback = soc_sdw_quirk_cb,51.matches = {52DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),53DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"),54},55.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),56},57{58.callback = soc_sdw_quirk_cb,59.matches = {60DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),61DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"),62},63.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),64},65{66.callback = soc_sdw_quirk_cb,67.matches = {68DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),69DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"),70},71.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),72},73{74.callback = soc_sdw_quirk_cb,75.matches = {76DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),77DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"),78},79.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),80},81{82.callback = soc_sdw_quirk_cb,83.matches = {84DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),85DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD3"),86},87.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),88},89{90.callback = soc_sdw_quirk_cb,91.matches = {92DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),93DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD4"),94},95.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),96},97{}98};99100static const struct snd_soc_ops sdw_ops = {101.startup = asoc_sdw_startup,102.prepare = asoc_sdw_prepare,103.trigger = asoc_sdw_trigger,104.hw_params = asoc_sdw_hw_params,105.hw_free = asoc_sdw_hw_free,106.shutdown = asoc_sdw_shutdown,107};108109static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};110111static int create_sdw_dailink(struct snd_soc_card *card,112struct asoc_sdw_dailink *soc_dai,113struct snd_soc_dai_link **dai_links,114int *be_id, struct snd_soc_codec_conf **codec_conf,115struct snd_soc_dai_link_component *sdw_platform_component)116{117struct device *dev = card->dev;118struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);119struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;120struct asoc_sdw_endpoint *soc_end;121int cpu_pin_id;122int stream;123int ret;124125list_for_each_entry(soc_end, &soc_dai->endpoints, list) {126if (soc_end->name_prefix) {127(*codec_conf)->dlc.name = soc_end->codec_name;128(*codec_conf)->name_prefix = soc_end->name_prefix;129(*codec_conf)++;130}131132if (soc_end->include_sidecar) {133ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf);134if (ret)135return ret;136}137}138139for_each_pcm_streams(stream) {140static const char * const sdw_stream_name[] = {141"SDW%d-PIN%d-PLAYBACK",142"SDW%d-PIN%d-CAPTURE",143"SDW%d-PIN%d-PLAYBACK-%s",144"SDW%d-PIN%d-CAPTURE-%s",145};146struct snd_soc_dai_link_ch_map *codec_maps;147struct snd_soc_dai_link_component *codecs;148struct snd_soc_dai_link_component *cpus;149int num_cpus = hweight32(soc_dai->link_mask[stream]);150int num_codecs = soc_dai->num_devs[stream];151int playback, capture;152int j = 0;153char *name;154155if (!soc_dai->num_devs[stream])156continue;157158soc_end = list_first_entry(&soc_dai->endpoints,159struct asoc_sdw_endpoint, list);160161*be_id = soc_end->dai_info->dailink[stream];162if (*be_id < 0) {163dev_err(dev, "Invalid dailink id %d\n", *be_id);164return -EINVAL;165}166167switch (amd_ctx->acp_rev) {168case ACP63_PCI_REV:169ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),170*be_id, &cpu_pin_id, dev);171if (ret)172return ret;173break;174case ACP70_PCI_REV:175case ACP71_PCI_REV:176case ACP72_PCI_REV:177ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),178*be_id, &cpu_pin_id, dev);179if (ret)180return ret;181break;182default:183return -EINVAL;184}185/* create stream name according to first link id */186if (ctx->append_dai_type) {187name = devm_kasprintf(dev, GFP_KERNEL,188sdw_stream_name[stream + 2],189ffs(soc_end->link_mask) - 1,190cpu_pin_id,191type_strings[soc_end->dai_info->dai_type]);192} else {193name = devm_kasprintf(dev, GFP_KERNEL,194sdw_stream_name[stream],195ffs(soc_end->link_mask) - 1,196cpu_pin_id);197}198if (!name)199return -ENOMEM;200201cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);202if (!cpus)203return -ENOMEM;204205codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);206if (!codecs)207return -ENOMEM;208209codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);210if (!codec_maps)211return -ENOMEM;212213list_for_each_entry(soc_end, &soc_dai->endpoints, list) {214if (!soc_end->dai_info->direction[stream])215continue;216217int link_num = ffs(soc_end->link_mask) - 1;218219cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,220"SDW%d Pin%d",221link_num, cpu_pin_id);222dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);223if (!cpus->dai_name)224return -ENOMEM;225226codec_maps[j].cpu = 0;227codec_maps[j].codec = j;228229codecs[j].name = soc_end->codec_name;230codecs[j].dai_name = soc_end->dai_info->dai_name;231j++;232}233234WARN_ON(j != num_codecs);235236playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);237capture = (stream == SNDRV_PCM_STREAM_CAPTURE);238239asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,240cpus, num_cpus, sdw_platform_component,2411, codecs, num_codecs,2420, asoc_sdw_rtd_init, &sdw_ops);243/*244* SoundWire DAILINKs use 'stream' functions and Bank Switch operations245* based on wait_for_completion(), tag them as 'nonatomic'.246*/247(*dai_links)->nonatomic = true;248(*dai_links)->ch_maps = codec_maps;249250list_for_each_entry(soc_end, &soc_dai->endpoints, list) {251if (soc_end->dai_info->init)252soc_end->dai_info->init(card, *dai_links,253soc_end->codec_info,254playback);255}256257(*dai_links)++;258}259260return 0;261}262263static int create_sdw_dailinks(struct snd_soc_card *card,264struct snd_soc_dai_link **dai_links, int *be_id,265struct asoc_sdw_dailink *soc_dais,266struct snd_soc_codec_conf **codec_conf)267{268struct device *dev = card->dev;269struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);270struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;271struct snd_soc_dai_link_component *sdw_platform_component;272int ret;273274sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),275GFP_KERNEL);276if (!sdw_platform_component)277return -ENOMEM;278279switch (amd_ctx->acp_rev) {280case ACP63_PCI_REV:281case ACP70_PCI_REV:282case ACP71_PCI_REV:283case ACP72_PCI_REV:284sdw_platform_component->name = "amd_ps_sdw_dma.0";285break;286default:287return -EINVAL;288}289290/* generate DAI links by each sdw link */291while (soc_dais->initialised) {292int current_be_id = 0;293294ret = create_sdw_dailink(card, soc_dais, dai_links,295¤t_be_id, codec_conf, sdw_platform_component);296if (ret)297return ret;298299/* Update the be_id to match the highest ID used for SDW link */300if (*be_id < current_be_id)301*be_id = current_be_id;302303soc_dais++;304}305306return 0;307}308309static int create_dmic_dailinks(struct snd_soc_card *card,310struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)311{312struct device *dev = card->dev;313struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);314struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;315struct snd_soc_dai_link_component *pdm_cpu;316struct snd_soc_dai_link_component *pdm_platform;317int ret;318319pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);320if (!pdm_cpu)321return -ENOMEM;322323pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);324if (!pdm_platform)325return -ENOMEM;326327switch (amd_ctx->acp_rev) {328case ACP63_PCI_REV:329case ACP70_PCI_REV:330case ACP71_PCI_REV:331case ACP72_PCI_REV:332pdm_cpu->name = "acp_ps_pdm_dma.0";333pdm_platform->name = "acp_ps_pdm_dma.0";334break;335default:336return -EINVAL;337}338339*be_id = ACP_DMIC_BE_ID;340ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",3410, 1, // DMIC only supports capture342pdm_cpu->name, pdm_platform->name,343"dmic-codec.0", "dmic-hifi", no_pcm,344asoc_sdw_dmic_init, NULL);345if (ret)346return ret;347348(*dai_links)++;349350return 0;351}352353static int soc_card_dai_links_create(struct snd_soc_card *card)354{355struct device *dev = card->dev;356struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);357int sdw_be_num = 0, dmic_num = 0;358struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);359struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;360struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;361struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;362struct snd_soc_codec_conf *codec_conf;363struct snd_soc_dai_link *dai_links;364int num_devs = 0;365int num_ends = 0;366int num_links;367int be_id = 0;368int ret;369370ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);371if (ret < 0) {372dev_err(dev, "failed to count devices/endpoints: %d\n", ret);373return ret;374}375376/* One per DAI link, worst case is a DAI link for every endpoint */377soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);378if (!soc_dais)379return -ENOMEM;380381/* One per endpoint, ie. each DAI on each codec/amp */382soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);383if (!soc_ends)384return -ENOMEM;385386ret = asoc_sdw_parse_sdw_endpoints(card, soc_dais, soc_ends, &num_devs);387if (ret < 0)388return ret;389390sdw_be_num = ret;391392/* enable dmic */393if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)394dmic_num = 1;395396dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);397398codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);399if (!codec_conf)400return -ENOMEM;401402/* allocate BE dailinks */403num_links = sdw_be_num + dmic_num;404dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);405if (!dai_links)406return -ENOMEM;407408card->codec_conf = codec_conf;409card->num_configs = num_devs;410card->dai_link = dai_links;411card->num_links = num_links;412413/* SDW */414if (sdw_be_num) {415ret = create_sdw_dailinks(card, &dai_links, &be_id,416soc_dais, &codec_conf);417if (ret)418return ret;419}420421/* dmic */422if (dmic_num > 0) {423if (ctx->ignore_internal_dmic) {424dev_warn(dev, "Ignoring ACP DMIC\n");425} else {426ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);427if (ret)428return ret;429}430}431432WARN_ON(codec_conf != card->codec_conf + card->num_configs);433WARN_ON(dai_links != card->dai_link + card->num_links);434435return ret;436}437438static int mc_probe(struct platform_device *pdev)439{440struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);441struct snd_soc_card *card;442struct amd_mc_ctx *amd_ctx;443struct asoc_sdw_mc_private *ctx;444int amp_num = 0, i;445int ret;446447amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);448if (!amd_ctx)449return -ENOMEM;450451amd_ctx->acp_rev = mach->mach_params.subsystem_rev;452amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;453ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);454if (!ctx)455return -ENOMEM;456ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();457ctx->private = amd_ctx;458card = &ctx->card;459card->dev = &pdev->dev;460card->name = "amd-soundwire";461card->owner = THIS_MODULE;462card->late_probe = asoc_sdw_card_late_probe;463464snd_soc_card_set_drvdata(card, ctx);465466dmi_check_system(soc_sdw_quirk_table);467468if (quirk_override != -1) {469dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",470soc_sdw_quirk, quirk_override);471soc_sdw_quirk = quirk_override;472}473474log_quirks(card->dev);475476ctx->mc_quirk = soc_sdw_quirk;477dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);478/* reset amp_num to ensure amp_num++ starts from 0 in each probe */479for (i = 0; i < ctx->codec_info_list_count; i++)480codec_info_list[i].amp_num = 0;481482ret = soc_card_dai_links_create(card);483if (ret < 0)484return ret;485486/*487* the default amp_num is zero for each codec and488* amp_num will only be increased for active amp489* codecs on used platform490*/491for (i = 0; i < ctx->codec_info_list_count; i++)492amp_num += codec_info_list[i].amp_num;493494card->components = devm_kasprintf(card->dev, GFP_KERNEL,495" cfg-amp:%d", amp_num);496if (!card->components)497return -ENOMEM;498if (mach->mach_params.dmic_num) {499card->components = devm_kasprintf(card->dev, GFP_KERNEL,500"%s mic:dmic cfg-mics:%d",501card->components,502mach->mach_params.dmic_num);503if (!card->components)504return -ENOMEM;505}506507/* Register the card */508ret = devm_snd_soc_register_card(card->dev, card);509if (ret) {510dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);511asoc_sdw_mc_dailink_exit_loop(card);512return ret;513}514515platform_set_drvdata(pdev, card);516517return ret;518}519520static void mc_remove(struct platform_device *pdev)521{522struct snd_soc_card *card = platform_get_drvdata(pdev);523524asoc_sdw_mc_dailink_exit_loop(card);525}526527static const struct platform_device_id mc_id_table[] = {528{ "amd_sdw", },529{}530};531MODULE_DEVICE_TABLE(platform, mc_id_table);532533static struct platform_driver soc_sdw_driver = {534.driver = {535.name = "amd_sdw",536.pm = &snd_soc_pm_ops,537},538.probe = mc_probe,539.remove = mc_remove,540.id_table = mc_id_table,541};542543module_platform_driver(soc_sdw_driver);544545MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");546MODULE_AUTHOR("Vijendar Mukunda <[email protected]>");547MODULE_LICENSE("GPL");548MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");549MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");550551552