diff options
Diffstat (limited to 'sound/soc/intel/boards/sof_board_helpers.c')
-rw-r--r-- | sound/soc/intel/boards/sof_board_helpers.c | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c new file mode 100644 index 000000000000..088894ff4165 --- /dev/null +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +#include <sound/soc.h> +#include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" +#include "sof_board_helpers.h" + +/* + * Intel HDMI DAI Link + */ +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); + + ctx->hdmi.hdmi_comp = dai->component; + + return 0; +} + +int sof_intel_board_card_late_probe(struct snd_soc_card *card) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + + if (!ctx->hdmi_num) + return 0; + + if (!ctx->hdmi.idisp_codec) + return 0; + + if (!ctx->hdmi.hdmi_comp) + return -EINVAL; + + return hda_dsp_hdmi_build_controls(card, ctx->hdmi.hdmi_comp); +} +EXPORT_SYMBOL_NS(sof_intel_board_card_late_probe, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +/* + * DMIC DAI Link + */ +static const struct snd_soc_dapm_widget dmic_widgets[] = { + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route dmic_routes[] = { + {"DMic", NULL, "SoC DMIC"}, +}; + +static int dmic_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, + ARRAY_SIZE(dmic_widgets)); + if (ret) { + dev_err(rtd->dev, "fail to add dmic widgets, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, dmic_routes, + ARRAY_SIZE(dmic_routes)); + if (ret) { + dev_err(rtd->dev, "fail to add dmic routes, ret %d\n", ret); + return ret; + } + + return 0; +} + +/* + * DAI Link Helpers + */ + +/* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */ +#define DEFAULT_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_CODEC, \ + SOF_LINK_DMIC01, \ + SOF_LINK_DMIC16K, \ + SOF_LINK_IDISP_HDMI, \ + SOF_LINK_AMP, \ + SOF_LINK_BT_OFFLOAD, \ + SOF_LINK_HDMI_IN) + +static struct snd_soc_dai_link_component dmic_component[] = { + { + .name = "dmic-codec", + .dai_name = "dmic-hifi", + } +}; + +static struct snd_soc_dai_link_component platform_component[] = { + { + /* name might be overridden during probe */ + .name = "0000:00:1f.3" + } +}; + +int sof_intel_board_set_codec_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec codec_type, int ssp_codec) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: codec %s, ssp %d\n", be_id, + sof_ssp_get_codec_name(codec_type), ssp_codec); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + if (soc_intel_is_byt() || soc_intel_is_cht()) { + /* backward-compatibility for BYT/CHT boards */ + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port", + ssp_codec); + } else { + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", + ssp_codec); + } + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs - caller to handle */ + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_codec_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_dmic_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_dmic_be_type be_type) +{ + struct snd_soc_dai_link_component *cpus; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + switch (be_type) { + case SOF_DMIC_01: + dev_dbg(dev, "link %d: dmic01\n", be_id); + + link->name = "dmic01"; + cpus->dai_name = "DMIC01 Pin"; + break; + case SOF_DMIC_16K: + dev_dbg(dev, "link %d: dmic16k\n", be_id); + + link->name = "dmic16k"; + cpus->dai_name = "DMIC16k Pin"; + break; + default: + dev_err(dev, "invalid be type %d\n", be_type); + return -EINVAL; + } + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + link->codecs = dmic_component; + link->num_codecs = ARRAY_SIZE(dmic_component); + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + if (be_type == SOF_DMIC_01) + link->init = dmic_init; + link->ignore_suspend = 1; + link->no_pcm = 1; + link->dpcm_capture = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_dmic_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_intel_hdmi_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int hdmi_id, bool idisp_codec) +{ + struct snd_soc_dai_link_component *cpus, *codecs; + + dev_dbg(dev, "link %d: intel hdmi, hdmi id %d, idisp codec %d\n", + be_id, hdmi_id, idisp_codec); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", hdmi_id); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", hdmi_id); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + if (idisp_codec) { + codecs = devm_kzalloc(dev, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!codecs) + return -ENOMEM; + + codecs->name = "ehdaudio0D2"; + codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "intel-hdmi-hifi%d", hdmi_id); + if (!codecs->dai_name) + return -ENOMEM; + + link->codecs = codecs; + } else { + link->codecs = &snd_soc_dummy_dlc; + } + link->num_codecs = 1; + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->init = (hdmi_id == 1) ? hdmi_init : NULL; + link->no_pcm = 1; + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_intel_hdmi_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_ssp_amp_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + enum sof_ssp_codec amp_type, int ssp_amp) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id, + sof_ssp_get_codec_name(amp_type), ssp_amp); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs - caller to handle */ + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; /* feedback stream or firmware-generated echo reference */ + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_ssp_amp_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_bt_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_bt) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + link->codecs = &snd_soc_dummy_dlc; + link->num_codecs = 1; + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + link->dpcm_playback = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_bt_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +int sof_intel_board_set_hdmi_in_link(struct device *dev, + struct snd_soc_dai_link *link, int be_id, + int ssp_hdmi) +{ + struct snd_soc_dai_link_component *cpus; + + dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi); + + /* link name */ + link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi); + if (!link->name) + return -ENOMEM; + + /* cpus */ + cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi); + if (!cpus->dai_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + + /* codecs */ + link->codecs = &snd_soc_dummy_dlc; + link->num_codecs = 1; + + /* platforms */ + link->platforms = platform_component; + link->num_platforms = ARRAY_SIZE(platform_component); + + link->id = be_id; + link->no_pcm = 1; + link->dpcm_capture = 1; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_hdmi_in_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +static int calculate_num_links(struct sof_card_private *ctx) +{ + int num_links = 0; + + /* headphone codec */ + if (ctx->codec_type != CODEC_NONE) + num_links++; + + /* dmic01 and dmic16k */ + if (ctx->dmic_be_num > 0) + num_links++; + + if (ctx->dmic_be_num > 1) + num_links++; + + /* idisp HDMI */ + num_links += ctx->hdmi_num; + + /* speaker amp */ + if (ctx->amp_type != CODEC_NONE) + num_links++; + + /* BT audio offload */ + if (ctx->bt_offload_present) + num_links++; + + /* HDMI-In */ + num_links += hweight32(ctx->ssp_mask_hdmi_in); + + return num_links; +} + +int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) +{ + struct snd_soc_dai_link *links; + int num_links; + int i; + int idx = 0; + int ret; + int ssp_hdmi_in = 0; + unsigned long link_order, link; + + num_links = calculate_num_links(ctx); + + links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), + GFP_KERNEL); + if (!links) + return -ENOMEM; + + if (ctx->link_order_overwrite) + link_order = ctx->link_order_overwrite; + else + link_order = DEFAULT_LINK_ORDER; + + dev_dbg(dev, "create dai links, link_order 0x%lx\n", link_order); + + while (link_order) { + link = link_order & SOF_LINK_ORDER_MASK; + link_order >>= SOF_LINK_ORDER_SHIFT; + + switch (link) { + case SOF_LINK_CODEC: + /* headphone codec */ + if (ctx->codec_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_codec_link(dev, &links[idx], + idx, + ctx->codec_type, + ctx->ssp_codec); + if (ret) { + dev_err(dev, "fail to set codec link, ret %d\n", + ret); + return ret; + } + + ctx->codec_link = &links[idx]; + idx++; + break; + case SOF_LINK_DMIC01: + /* dmic01 */ + if (ctx->dmic_be_num == 0) + continue; + + /* at least we have dmic01 */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_01); + if (ret) { + dev_err(dev, "fail to set dmic01 link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_DMIC16K: + /* dmic16k */ + if (ctx->dmic_be_num <= 1) + continue; + + /* set up 2 BE links at most */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_16K); + if (ret) { + dev_err(dev, "fail to set dmic16k link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_IDISP_HDMI: + /* idisp HDMI */ + for (i = 1; i <= ctx->hdmi_num; i++) { + ret = sof_intel_board_set_intel_hdmi_link(dev, + &links[idx], + idx, i, + ctx->hdmi.idisp_codec); + if (ret) { + dev_err(dev, "fail to set hdmi link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_AMP: + /* speaker amp */ + if (ctx->amp_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], + idx, + ctx->amp_type, + ctx->ssp_amp); + if (ret) { + dev_err(dev, "fail to set amp link, ret %d\n", + ret); + return ret; + } + + ctx->amp_link = &links[idx]; + idx++; + break; + case SOF_LINK_BT_OFFLOAD: + /* BT audio offload */ + if (!ctx->bt_offload_present) + continue; + + ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, + ctx->ssp_bt); + if (ret) { + dev_err(dev, "fail to set bt link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_HDMI_IN: + /* HDMI-In */ + for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { + ret = sof_intel_board_set_hdmi_in_link(dev, + &links[idx], + idx, + ssp_hdmi_in); + if (ret) { + dev_err(dev, "fail to set hdmi-in link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_NONE: + /* caught here if it's not used as terminator in macro */ + fallthrough; + default: + dev_err(dev, "invalid link type %ld\n", link); + return -EINVAL; + } + } + + if (idx != num_links) { + dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx, + num_links); + return -EINVAL; + } + + card->dai_link = links; + card->num_links = num_links; + + return 0; +} +EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, + const char * const dai_name[], int num_dais) +{ + struct snd_soc_dai *dai; + int index; + int i; + + for (index = 0; index < num_dais; index++) + for_each_rtd_codec_dais(rtd, i, dai) + if (strstr(dai->name, dai_name[index])) { + dev_dbg(rtd->card->dev, "get dai %s\n", dai->name); + return dai; + } + + return NULL; +} +EXPORT_SYMBOL_NS(get_codec_dai_by_name, SND_SOC_INTEL_SOF_BOARD_HELPERS); + +MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers"); +MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON); |