// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) // // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // // Copyright(c) 2021 Intel Corporation // //
/** * sof_comp_alloc - allocate and initialize buffer for a new component * @swidget: pointer to struct snd_sof_widget containing extended data * @ipc_size: IPC payload size that will be updated depending on valid * extended data. * @index: ID of the pipeline the component belongs to * * Return: The pointer to the new allocated component, NULL if failed.
*/ staticvoid *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, int index)
{ struct sof_ipc_comp *comp;
size_t total_size = *ipc_size;
size_t ext_size = sizeof(swidget->uuid);
/* only non-zero UUID is valid */ if (!guid_is_null(&swidget->uuid))
total_size += ext_size;
comp = kzalloc(total_size, GFP_KERNEL); if (!comp) return NULL;
/* handle the extended data if needed */ if (total_size > *ipc_size) { /* append extended data to the end of the component */
memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size);
comp->ext_data_length = ext_size;
}
/* parse one set of pcm_tokens */
ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*host), 1); if (ret < 0) goto err;
/* parse one set of comp_tokens */
ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(host->config), 1); if (ret < 0) goto err;
/* component at start of pipeline is our stream id */
comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname); if (!comp_swidget) {
dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n",
swidget->widget->name, swidget->widget->sname);
ret = -EINVAL; goto err;
}
pipeline->sched_id = comp_swidget->comp_id;
/* parse one set of scheduler tokens */
ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*pipeline), 1); if (ret < 0) goto err;
/* parse one set of pipeline tokens */
ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*swidget), 1); if (ret < 0) goto err;
if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
pipeline->core = SOF_DSP_PRIMARY_CORE;
if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))
swidget->dynamic_pipeline_widget =
sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE);
/* parse one set of src tokens */
ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*src), 1); if (ret < 0) goto err;
/* parse one set of comp tokens */
ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS,
swidget->tuples, swidget->num_tuples, sizeof(src->config), 1); if (ret < 0) goto err;
/* parse one set of asrc tokens */
ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*asrc), 1); if (ret < 0) goto err;
/* parse one set of comp tokens */
ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS,
swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1); if (ret < 0) goto err;
/* parse one set of volume tokens */
ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*volume), 1); if (ret < 0) goto err;
/* parse one set of comp tokens */
ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS,
swidget->tuples, swidget->num_tuples, sizeof(volume->config), 1); if (ret < 0) goto err;
for (i = 0; i < widget->num_kcontrols; i++) {
kc = &widget->kcontrol_news[i];
switch (widget->dobj.widget.kcontrol_type[i]) { case SND_SOC_TPLG_TYPE_MIXER:
sm = (struct soc_mixer_control *)kc->private_value;
wdata[i].control = sm->dobj.private; break; case SND_SOC_TPLG_TYPE_BYTES:
sbe = (struct soc_bytes_ext *)kc->private_value;
wdata[i].control = sbe->dobj.private; break; case SND_SOC_TPLG_TYPE_ENUM:
se = (struct soc_enum *)kc->private_value;
wdata[i].control = se->dobj.private; break; default:
dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n",
widget->dobj.widget.kcontrol_type[i], widget->name); return -EINVAL;
}
if (!wdata[i].control) {
dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name); return -EINVAL;
}
cdata = wdata[i].control->ipc_control_data;
if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) { /* make sure data is valid - data can be updated at runtime */ if (cdata->data->magic != SOF_ABI_MAGIC) return -EINVAL;
wdata[i].pdata = cdata->data->data;
wdata[i].pdata_size = cdata->data->size;
} else { /* points to the control data union */
wdata[i].pdata = cdata->chanv; /* * wdata[i].control->size is calculated with struct_size * and includes the size of struct sof_ipc_ctrl_data
*/
wdata[i].pdata_size = wdata[i].control->size - sizeof(struct sof_ipc_ctrl_data);
}
*size += wdata[i].pdata_size;
/* get data type */ switch (cdata->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH:
wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE;
wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; break; case SOF_CTRL_CMD_BINARY:
wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA;
wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; break; default: break;
}
}
return 0;
}
staticint sof_process_load(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, int type)
{ struct snd_soc_dapm_widget *widget = swidget->widget; struct sof_ipc_comp_process *process; struct sof_widget_data *wdata = NULL;
size_t ipc_data_size = 0;
size_t ipc_size; int offset = 0; int ret; int i;
/* allocate struct for widget control data sizes and types */ if (widget->num_kcontrols) {
wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL); if (!wdata) return -ENOMEM;
/* get possible component controls and get size of all pdata */
ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size); if (ret < 0) goto out;
}
/* we are exceeding max ipc size, config needs to be sent separately */ if (ipc_size > SOF_IPC_MSG_MAX_SIZE) {
ipc_size -= ipc_data_size;
ipc_data_size = 0;
}
process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); if (!process) {
ret = -ENOMEM; goto out;
}
/* parse one set of comp tokens */
ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS,
swidget->tuples, swidget->num_tuples, sizeof(process->config), 1); if (ret < 0) goto err;
dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name);
sof_dbg_comp_config(scomp, &process->config);
/* * found private data in control, so copy it. * get possible component controls - get size of all pdata, * then memcpy with headers
*/ if (ipc_data_size) { for (i = 0; i < widget->num_kcontrols; i++) { if (!wdata[i].pdata_size) continue;
/* parse one set of process tokens */
ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(config), 1); if (ret < 0) return ret;
/* now load process specific data and send IPC */ return sof_process_load(scomp, swidget, find_process_comp_type(config.type));
}
/* parse one set of HDA tokens */
ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples,
slink->num_tuples, size, 1); if (ret < 0) return ret;
/* parse one set of SAI tokens */
ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples,
slink->num_tuples, size, 1); if (ret < 0) return ret;
/* parse one set of ESAI tokens */
ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples,
slink->num_tuples, size, 1); if (ret < 0) return ret;
/* handle master/slave and inverted clocks */
sof_dai_set_format(hw_config, config);
config->hdr.size = size;
/* parse the required set of MICFIL PDM tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->micfil, SOF_MICFIL_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* handle master/slave and inverted clocks */
sof_dai_set_format(hw_config, config);
config->hdr.size = size;
/* parse the required set of ACPDMIC tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->acpdmic, SOF_ACPDMIC_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* parse the required set of ACP_SDW tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* set config for all DAI's with name matching the link name */
dai->number_configs = 1;
dai->current_config = 0;
private->dai_config = kmemdup(config, size, GFP_KERNEL); if (!private->dai_config) return -ENOMEM;
/* parse the required set of AFE tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* * Parse common data, we should have 1 common data per hw_config.
*/
ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* process all possible hw configs */ for (i = 0; i < slink->num_hw_configs; i++) { if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id)
current_config = i;
/* handle master/slave and inverted clocks */
sof_dai_set_format(&hw_config[i], &config[i]);
config[i].hdr.size = size;
if (sdev->mclk_id_override) {
dev_dbg(scomp->dev, "tplg: overriding topology mclk_id %d by quirk %d\n",
config[i].ssp.mclk_id, sdev->mclk_id_quirk);
config[i].ssp.mclk_id = sdev->mclk_id_quirk;
}
/* Ensure the entire DMIC config struct is zeros */
memset(&config->dmic, 0, sizeof(config->dmic));
/* parse the required set of DMIC tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* parse the required set of DMIC PDM tokens based on number of active PDM's */
ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS,
slink->tuples, slink->num_tuples, sizeof(struct sof_ipc_dai_dmic_pdm_ctrl),
config->dmic.num_pdm_active); if (ret < 0) return ret;
/* set IPC header size */
config->hdr.size = size;
for (i = 0; i < config->dmic.num_pdm_active; i++) {
dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n",
config->dmic.pdm[i].id,
config->dmic.pdm[i].enable_mic_a,
config->dmic.pdm[i].enable_mic_b);
dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n",
config->dmic.pdm[i].id,
config->dmic.pdm[i].polarity_mic_a,
config->dmic.pdm[i].polarity_mic_b);
dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n",
config->dmic.pdm[i].id,
config->dmic.pdm[i].clk_edge,
config->dmic.pdm[i].skew);
}
/* * this takes care of backwards compatible handling of fifo_bits_b. * It is deprecated since firmware ABI version 3.0.1.
*/ if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1))
config->dmic.fifo_bits_b = config->dmic.fifo_bits;
/* parse the required set of ALH tokens based on num_hw_cfgs */
ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples,
slink->num_tuples, size, slink->num_hw_configs); if (ret < 0) return ret;
/* init IPC */
config->hdr.size = size;
/* set config for all DAI's with name matching the link name */
dai->number_configs = 1;
dai->current_config = 0;
private->dai_config = kmemdup(config, size, GFP_KERNEL); if (!private->dai_config) return -ENOMEM;
/* parse one set of DAI tokens */
ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*comp_dai), 1); if (ret < 0) goto free_comp;
/* update comp_tokens */
ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS,
swidget->tuples, swidget->num_tuples, sizeof(comp_dai->config), 1); if (ret < 0) goto free_comp;
/* Subtract the base to match the FW dai index. */ if (comp_dai->type == SOF_DAI_INTEL_ALH) { if (comp_dai->dai_index < INTEL_ALH_DAI_INDEX_BASE) {
dev_err(sdev->dev, "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n",
comp_dai->dai_index, INTEL_ALH_DAI_INDEX_BASE);
ret = -EINVAL; goto free_comp;
}
comp_dai->dai_index -= INTEL_ALH_DAI_INDEX_BASE;
}
dev_dbg(scomp->dev, "dai %s: type %d index %d\n",
swidget->widget->name, comp_dai->type, comp_dai->dai_index);
sof_dbg_comp_config(scomp, &comp_dai->config);
/* now update DAI config */
list_for_each_entry(slink, &sdev->dai_link_list, list) { struct sof_ipc_dai_config common_config; int i;
if (strcmp(slink->link->name, dai->name)) continue;
/* Reserve memory for all hw configs, eventually freed by widget */
config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL); if (!config) {
ret = -ENOMEM; goto free_comp;
}
/* parse one set of DAI link tokens */
ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS,
slink->tuples, slink->num_tuples, sizeof(common_config), 1); if (ret < 0) goto free_config;
for (i = 0; i < slink->num_hw_configs; i++) {
config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
config[i].format = le32_to_cpu(slink->hw_configs[i].fmt);
config[i].type = common_config.type;
config[i].dai_index = comp_dai->dai_index;
}
switch (common_config.type) { case SOF_DAI_INTEL_SSP:
ret = sof_link_ssp_load(scomp, slink, config, dai); break; case SOF_DAI_INTEL_DMIC:
ret = sof_link_dmic_load(scomp, slink, config, dai); break; case SOF_DAI_INTEL_HDA:
ret = sof_link_hda_load(scomp, slink, config, dai); break; case SOF_DAI_INTEL_ALH:
ret = sof_link_alh_load(scomp, slink, config, dai); break; case SOF_DAI_IMX_SAI:
ret = sof_link_sai_load(scomp, slink, config, dai); break; case SOF_DAI_IMX_ESAI:
ret = sof_link_esai_load(scomp, slink, config, dai); break; case SOF_DAI_IMX_MICFIL:
ret = sof_link_micfil_load(scomp, slink, config, dai); break; case SOF_DAI_AMD_BT:
ret = sof_link_acp_bt_load(scomp, slink, config, dai); break; case SOF_DAI_AMD_SP: case SOF_DAI_AMD_SP_VIRTUAL:
ret = sof_link_acp_sp_load(scomp, slink, config, dai); break; case SOF_DAI_AMD_HS: case SOF_DAI_AMD_HS_VIRTUAL:
ret = sof_link_acp_hs_load(scomp, slink, config, dai); break; case SOF_DAI_AMD_DMIC:
ret = sof_link_acp_dmic_load(scomp, slink, config, dai); break; case SOF_DAI_MEDIATEK_AFE:
ret = sof_link_afe_load(scomp, slink, config, dai); break; case SOF_DAI_AMD_SDW:
ret = sof_link_acp_sdw_load(scomp, slink, config, dai); break; default: break;
} if (ret < 0) {
dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name); goto free_config;
}
if (cdata->data->magic != SOF_ABI_MAGIC) {
dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);
ret = -EINVAL; goto err;
}
if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n",
cdata->data->abi);
ret = -EINVAL; goto err;
}
priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr); if (priv_size_check != scontrol->priv_size) {
dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n",
priv_size_check, scontrol->priv_size);
ret = -EINVAL; goto err;
}
}
/* set cmd for mixer control */ if (scontrol->max == 1) {
cdata->cmd = SOF_CTRL_CMD_SWITCH; return 0;
}
cdata->cmd = SOF_CTRL_CMD_VOLUME;
/* set default volume values to 0dB in control */ for (i = 0; i < scontrol->num_channels; i++) {
cdata->chanv[i].channel = i;
cdata->chanv[i].value = VOL_ZERO_DB;
}
/* set format */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16:
pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; break; case SNDRV_PCM_FORMAT_S24:
pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; break; case SNDRV_PCM_FORMAT_S32:
pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; break; default: return -EINVAL;
}
/* send IPC to the DSP */
ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm)); if (ret < 0)
dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
swidget->widget->name);
/* send IPC to the DSP */
ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
return ret;
}
staticint sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)
{ struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *scomp; int stream = SNDRV_PCM_STREAM_CAPTURE; struct snd_sof_pcm *spcm; int ret = 0;
if (!swidget) return 0;
scomp = swidget->scomp;
dev_dbg(scomp->dev, "received event %d for widget %s\n",
event, w->name);
/* get runtime PCM params using widget's stream name */
spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); if (!spcm) {
dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__,
swidget->widget->name); return -EINVAL;
}
/* process events */ switch (event) { case SND_SOC_DAPM_PRE_PMU: if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); return 0;
}
/* set pcm params */
ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream); if (ret < 0) {
dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n",
__func__, swidget->widget->name); break;
}
/* start trigger */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START); if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
swidget->widget->name); break; case SND_SOC_DAPM_POST_PMD: if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev, "POST_PMD event ignored, KWD pipeline will remain RUNNING\n"); return 0;
}
/* stop trigger */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
swidget->widget->name);
/* pcm free */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); if (ret < 0)
dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__,
swidget->widget->name); break; default: break;
}
/* validate widget event type */ switch (event_type) { case SOF_KEYWORD_DETECT_DAPM_EVENT: /* only KEYWORD_DETECT comps should handle this */ if (swidget->id != snd_soc_dapm_effect) break;
ipc_comp = swidget->private; if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) break;
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.