part_id = SDW_PART_ID(adr);
sdw_version = SDW_VERSION(adr); for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) /* * A codec info is for all sdw version with the part id if * version_id is not specified in the codec info.
*/ if (part_id == codec_info_list[i].part_id &&
(!codec_info_list[i].version_id ||
sdw_version == codec_info_list[i].version_id)) return &codec_info_list[i];
int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
{ struct snd_soc_card *card = rtd->card; struct asoc_sdw_codec_info *codec_info; struct snd_soc_dai *dai; constchar *spk_components=""; int dai_index; int ret; int i;
for_each_rtd_codec_dais(rtd, i, dai) {
codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index); if (!codec_info) return -EINVAL;
/* * A codec dai can be connected to different dai links for capture and playback, * but we only need to call the rtd_init function once. * The rtd_init for each codec dai is independent. So, the order of rtd_init * doesn't matter.
*/ if (codec_info->dais[dai_index].rtd_init_done) continue;
/* * Add card controls and dapm widgets for the first codec dai. * The controls and widgets will be used for all codec dais.
*/
if (i > 0) goto skip_add_controls_widgets;
if (codec_info->dais[dai_index].controls) {
ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
codec_info->dais[dai_index].num_controls); if (ret) {
dev_err(card->dev, "%#x controls addition failed: %d\n",
codec_info->part_id, ret); return ret;
}
} if (codec_info->dais[dai_index].widgets) {
ret = snd_soc_dapm_new_controls(&card->dapm,
codec_info->dais[dai_index].widgets,
codec_info->dais[dai_index].num_widgets); if (ret) {
dev_err(card->dev, "%#x widgets addition failed: %d\n",
codec_info->part_id, ret); return ret;
}
}
skip_add_controls_widgets: if (codec_info->dais[dai_index].rtd_init) {
ret = codec_info->dais[dai_index].rtd_init(rtd, dai); if (ret) return ret;
}
/* Generate the spk component string for card->components string */ if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
codec_info->dais[dai_index].component_name) { if (strlen (spk_components) == 0)
spk_components =
devm_kasprintf(card->dev, GFP_KERNEL, "%s",
codec_info->dais[dai_index].component_name); else /* Append component name to spk_components */
spk_components =
devm_kasprintf(card->dev, GFP_KERNEL, "%s+%s", spk_components,
codec_info->dais[dai_index].component_name);
}
codec_info->dais[dai_index].rtd_init_done = true;
}
if (strlen (spk_components) > 0) { /* Update card components for speaker components */
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
card->components, spk_components); if (!card->components) return -ENOMEM;
}
/* these wrappers are only needed to avoid typecast compilation errors */ int asoc_sdw_startup(struct snd_pcm_substream *substream)
{ return sdw_startup_stream(substream);
}
EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
/* Find stream from first CPU DAI */
dai = snd_soc_rtd_to_cpu(rtd, 0);
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream);
}
int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sdw_stream_runtime *sdw_stream; struct snd_soc_dai *dai; int ret;
/* Find stream from first CPU DAI */
dai = snd_soc_rtd_to_cpu(rtd, 0);
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream);
}
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME:
ret = sdw_enable_stream(sdw_stream); break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP:
ret = sdw_disable_stream(sdw_stream); break; default:
ret = -EINVAL; break;
}
int asoc_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai_link_ch_map *ch_maps; int ch = params_channels(params); unsignedint ch_mask; int num_codecs; int step; int i;
if (!rtd->dai_link->ch_maps) return 0;
/* Identical data will be sent to all codecs in playback */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ch_mask = GENMASK(ch - 1, 0);
step = 0;
} else {
num_codecs = rtd->dai_link->num_codecs;
if (ch < num_codecs || ch % num_codecs != 0) {
dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
ch, num_codecs); return -EINVAL;
}
/* * The captured data will be combined from each cpu DAI if the dai * link has more than one codec DAIs. Set codec channel mask and * ASoC will set the corresponding channel numbers for each cpu dai.
*/
for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
ch_maps->ch_mask = ch_mask << (i * step);
/* Find stream from first CPU DAI */
dai = snd_soc_rtd_to_cpu(rtd, 0);
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) {
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream);
}
/* helper to get the link that the codec DAI is used */ struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card, constchar *dai_name)
{ struct snd_soc_dai_link *dai_link; int i; int j;
for_each_card_prelinks(card, i, dai_link) { for (j = 0; j < dai_link->num_codecs; j++) { /* Check each codec in a link */ if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) return dai_link;
}
} return NULL;
}
EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
{ struct snd_soc_dai_link *dai_link; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; int i, j;
for (i = 0; i < ctx->codec_info_list_count; i++) { for (j = 0; j < codec_info_list[i].dai_num; j++) {
codec_info_list[i].dais[j].rtd_init_done = false; /* Check each dai in codec_info_lis to see if it is used in the link */ if (!codec_info_list[i].dais[j].exit) continue; /* * We don't need to call .exit function if there is no matched * dai link found.
*/
dai_link = asoc_sdw_mc_find_codec_dai_used(card,
codec_info_list[i].dais[j].dai_name); if (dai_link) { /* Do the .exit function if the codec dai is used in the link */
ret = codec_info_list[i].dais[j].exit(card, dai_link); if (ret)
dev_warn(card->dev, "codec exit failed %d\n",
ret); break;
}
}
}
}
EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
int asoc_sdw_card_late_probe(struct snd_soc_card *card)
{ int ret = 0; int i;
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { if (codec_info_list[i].codec_card_late_probe) {
ret = codec_info_list[i].codec_card_late_probe(card); if (ret < 0) return ret;
}
} return ret;
}
EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, int *be_id, char *name, int playback, int capture, struct snd_soc_dai_link_component *cpus, int cpus_num, struct snd_soc_dai_link_component *platform_component, int num_platforms, struct snd_soc_dai_link_component *codecs, int codecs_num, int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd), conststruct snd_soc_ops *ops)
{
dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
dai_links->id = (*be_id)++;
dai_links->name = name;
dai_links->stream_name = name;
dai_links->platforms = platform_component;
dai_links->num_platforms = num_platforms;
dai_links->no_pcm = no_pcm;
dai_links->cpus = cpus;
dai_links->num_cpus = cpus_num;
dai_links->codecs = codecs;
dai_links->num_codecs = codecs_num;
dai_links->playback_only = playback && !capture;
dai_links->capture_only = !playback && capture;
dai_links->init = init;
dai_links->ops = ops;
}
EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, int *be_id, char *name, int playback, int capture, constchar *cpu_dai_name, constchar *platform_comp_name, constchar *codec_name, constchar *codec_dai_name, int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd), conststruct snd_soc_ops *ops)
{ struct snd_soc_dai_link_component *dlc;
/* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL); if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name) return -ENOMEM;
staticint asoc_sdw_get_dai_type(u32 type)
{ switch (type) { case SDCA_FUNCTION_TYPE_SMART_AMP: case SDCA_FUNCTION_TYPE_SIMPLE_AMP: return SOC_SDW_DAI_TYPE_AMP; case SDCA_FUNCTION_TYPE_SMART_MIC: case SDCA_FUNCTION_TYPE_SIMPLE_MIC: case SDCA_FUNCTION_TYPE_SPEAKER_MIC: return SOC_SDW_DAI_TYPE_MIC; case SDCA_FUNCTION_TYPE_UAJ: case SDCA_FUNCTION_TYPE_RJ: case SDCA_FUNCTION_TYPE_SIMPLE_JACK: return SOC_SDW_DAI_TYPE_JACK; default: return -EINVAL;
}
}
/* * Check if the SDCA endpoint is present by the SDW peripheral * * @dev: Device pointer * @codec_info: Codec info pointer * @adr_link: ACPI link address * @adr_index: Index of the ACPI link address * @end_index: Index of the endpoint * * Return: 1 if the endpoint is present, * 0 if the endpoint is not present, * negative error code.
*/
dlc->dai_name = dai_info->dai_name;
codec_dai = snd_soc_find_dai_with_mutex(dlc); if (!codec_dai) {
dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
kfree(dlc); return -EPROBE_DEFER;
}
kfree(dlc);
sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info,
adr_link, adr_index); if (!sdw_codec_name) return -ENOMEM;
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); if (!sdw_dev) {
dev_err(dev, "codec %s not found\n", sdw_codec_name); return -EINVAL;
}
slave = dev_to_sdw_dev(sdw_dev); if (!slave) {
ret = -EINVAL; goto put_device;
}
/* Make sure BIOS provides SDCA properties */ if (!slave->sdca_data.interface_revision) {
dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
ret = 1; goto put_device;
}
for (i = 0; i < slave->sdca_data.num_functions; i++) { int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
if (dai_type == dai_info->dai_type) {
dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
dai_type, slave->sdca_data.function[i].name);
ret = 1; goto put_device;
}
}
dev_dbg(&slave->dev, "SDCA device function for DAI type %d not supported, skip endpoint\n",
dai_info->dai_type);
ret = 0;
put_device:
put_device(sdw_dev); return ret;
}
int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs)
{ struct device *dev = card->dev; struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; conststruct snd_soc_acpi_link_adr *adr_link; struct asoc_sdw_endpoint *soc_end = soc_ends; int num_dais = 0; int i, j; int ret;
for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { int num_link_dailinks = 0;
if (!is_power_of_2(adr_link->mask)) {
dev_err(dev, "link with multiple mask bits: 0x%x\n",
adr_link->mask); return -EINVAL;
}
for (i = 0; i < adr_link->num_adr; i++) { conststruct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; struct asoc_sdw_codec_info *codec_info; constchar *codec_name; bool check_sdca = false;
if (!adr_dev->name_prefix) {
dev_err(dev, "codec 0x%llx does not have a name prefix\n",
adr_dev->adr); return -EINVAL;
}
codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); if (!codec_info) return -EINVAL;
/* * quirk should have higher priority than the sdca properties * in the BIOS. We can't always check the DAI quirk because we * will set the mc_quirk when the BIOS doesn't provide the right * information. The endpoint will be skipped if the dai_info-> * quirk_exclude and mc_quirk are both not set if we always skip * the endpoint according to the quirk information. We need to * keep the endpoint if it is present in the BIOS. So, only * check the DAI quirk when the mc_quirk is set or SDCA endpoint * present check is not needed.
*/ if (dai_info->quirk & ctx->mc_quirk || !check_sdca) { /* * Check the endpoint if a matching quirk is set or SDCA * endpoint check is not necessary
*/ if (dai_info->quirk &&
!(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) continue;
} else { /* Check SDCA codec endpoint if there is no matching quirk */
ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j); if (ret < 0) return ret;
/* The endpoint is not present, skip */ if (!ret) continue;
}
if (adr_end->num >= codec_info->dai_num) {
dev_err(dev, "%d is too many endpoints for codec: 0x%x\n",
adr_end->num, codec_info->part_id); return -EINVAL;
}
for_each_pcm_streams(stream) { if (dai_info->direction[stream] &&
dai_info->dailink[stream] < 0) {
dev_err(dev, "Invalid dailink id %d for codec: 0x%x\n",
dai_info->dailink[stream],
codec_info->part_id); return -EINVAL;
}
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.