Path: blob/master/sound/soc/amd/acp/acp-sdw-legacy-mach.c
54337 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 snd_soc_aux_dev *soc_aux;361struct snd_soc_codec_conf *codec_conf;362struct snd_soc_dai_link *dai_links;363int num_devs = 0;364int num_ends = 0;365int num_aux = 0;366int num_confs;367int num_links;368int be_id = 0;369int ret;370371ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);372if (ret < 0) {373dev_err(dev, "failed to count devices/endpoints: %d\n", ret);374return ret;375}376377num_confs = num_ends;378379/* One per DAI link, worst case is a DAI link for every endpoint */380struct asoc_sdw_dailink *soc_dais __free(kfree) =381kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);382if (!soc_dais)383return -ENOMEM;384385/* One per endpoint, ie. each DAI on each codec/amp */386struct asoc_sdw_endpoint *soc_ends __free(kfree) =387kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);388if (!soc_ends)389return -ENOMEM;390391soc_aux = devm_kcalloc(dev, num_aux, sizeof(*soc_aux), GFP_KERNEL);392if (!soc_aux)393return -ENOMEM;394395ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs);396if (ret < 0)397return ret;398399sdw_be_num = ret;400401/* enable dmic */402if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)403dmic_num = 1;404405dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);406407codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);408if (!codec_conf)409return -ENOMEM;410411/* allocate BE dailinks */412num_links = sdw_be_num + dmic_num;413dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);414if (!dai_links)415return -ENOMEM;416417card->codec_conf = codec_conf;418card->num_configs = num_confs;419card->dai_link = dai_links;420card->num_links = num_links;421card->aux_dev = soc_aux;422card->num_aux_devs = num_aux;423424/* SDW */425if (sdw_be_num) {426ret = create_sdw_dailinks(card, &dai_links, &be_id,427soc_dais, &codec_conf);428if (ret)429return ret;430}431432/* dmic */433if (dmic_num > 0) {434if (ctx->ignore_internal_dmic) {435dev_warn(dev, "Ignoring ACP DMIC\n");436} else {437ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);438if (ret)439return ret;440}441}442443WARN_ON(codec_conf != card->codec_conf + card->num_configs);444WARN_ON(dai_links != card->dai_link + card->num_links);445446return ret;447}448449static int mc_probe(struct platform_device *pdev)450{451struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);452struct snd_soc_card *card;453struct amd_mc_ctx *amd_ctx;454struct asoc_sdw_mc_private *ctx;455int amp_num = 0, i;456int ret;457458amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);459if (!amd_ctx)460return -ENOMEM;461462amd_ctx->acp_rev = mach->mach_params.subsystem_rev;463amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;464ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);465if (!ctx)466return -ENOMEM;467ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();468ctx->private = amd_ctx;469card = &ctx->card;470card->dev = &pdev->dev;471card->name = "amd-soundwire";472card->owner = THIS_MODULE;473card->late_probe = asoc_sdw_card_late_probe;474475snd_soc_card_set_drvdata(card, ctx);476if (mach->mach_params.subsystem_id_set)477snd_soc_card_set_pci_ssid(card,478mach->mach_params.subsystem_vendor,479mach->mach_params.subsystem_device);480481dmi_check_system(soc_sdw_quirk_table);482483if (quirk_override != -1) {484dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",485soc_sdw_quirk, quirk_override);486soc_sdw_quirk = quirk_override;487}488489log_quirks(card->dev);490491ctx->mc_quirk = soc_sdw_quirk;492dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);493/* reset amp_num to ensure amp_num++ starts from 0 in each probe */494for (i = 0; i < ctx->codec_info_list_count; i++)495codec_info_list[i].amp_num = 0;496497ret = soc_card_dai_links_create(card);498if (ret < 0)499return ret;500501/*502* the default amp_num is zero for each codec and503* amp_num will only be increased for active amp504* codecs on used platform505*/506for (i = 0; i < ctx->codec_info_list_count; i++)507amp_num += codec_info_list[i].amp_num;508509card->components = devm_kasprintf(card->dev, GFP_KERNEL,510" cfg-amp:%d", amp_num);511if (!card->components)512return -ENOMEM;513if (mach->mach_params.dmic_num) {514card->components = devm_kasprintf(card->dev, GFP_KERNEL,515"%s mic:dmic cfg-mics:%d",516card->components,517mach->mach_params.dmic_num);518if (!card->components)519return -ENOMEM;520}521522/* Register the card */523ret = devm_snd_soc_register_card(card->dev, card);524if (ret) {525dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);526asoc_sdw_mc_dailink_exit_loop(card);527return ret;528}529530platform_set_drvdata(pdev, card);531532return ret;533}534535static void mc_remove(struct platform_device *pdev)536{537struct snd_soc_card *card = platform_get_drvdata(pdev);538539asoc_sdw_mc_dailink_exit_loop(card);540}541542static const struct platform_device_id mc_id_table[] = {543{ "amd_sdw", },544{}545};546MODULE_DEVICE_TABLE(platform, mc_id_table);547548static struct platform_driver soc_sdw_driver = {549.driver = {550.name = "amd_sdw",551.pm = &snd_soc_pm_ops,552},553.probe = mc_probe,554.remove = mc_remove,555.id_table = mc_id_table,556};557558module_platform_driver(soc_sdw_driver);559560MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");561MODULE_AUTHOR("Vijendar Mukunda <[email protected]>");562MODULE_LICENSE("GPL");563MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");564MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");565566567