Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/sound/soc/amd/acp/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  acp-sdw-sof-mach.c   Sprache: C

 
// 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 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;
  case ACP70_PCI_REV:
  case ACP71_PCI_REV:
  case ACP72_PCI_REV:
   ret = get_acp70_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);
   if (!cpus->dai_name)
    return -ENOMEM;
   dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);

   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,
           1, 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 = 0;

  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, int no_pcm)
{
 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,
         "dmic-codec""dmic-hifi", no_pcm,
         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 asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
 struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
 struct snd_soc_codec_conf *codec_conf;
 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)
  return -ENOMEM;

 ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
 if (ret < 0)
  return ret;

 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)
  return -ENOMEM;

 /* 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)
  return -ENOMEM;

 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)
   return ret;
 }

 /* 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, 1);
   if (ret)
    return ret;
  }
 }

 WARN_ON(codec_conf != card->codec_conf + card->num_configs);
 WARN_ON(dai_links != card->dai_link + card->num_links);

 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");
MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");

Messung V0.5
C=97 H=98 G=97

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.