diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 5fd2486582cd49..a495ae21c680f6 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -188,6 +188,12 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) +#define SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SET_SHIFT 24 +#define SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SET(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SET_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SHIFT 25 +#define SOF_IPC4_GLB_PIPE_EXT_DIRECTION(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SHIFT) + /* pipeline set state ipc msg */ #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 020e7a71f26ceb..a9f6eade4880cd 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1063,6 +1063,52 @@ static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget) return ret; } +static int sof_ipc4_widget_setup_comp_siggen(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_pipeline *spipe = swidget->spipe; + struct sof_ipc4_siggen *siggen; + int ret; + + dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); + + siggen = kzalloc(sizeof(*siggen), GFP_KERNEL); + if (!siggen) + return -ENOMEM; + + swidget->private = siggen; + + ret = sof_ipc4_get_audio_fmt(scomp, swidget, &siggen->available_fmt, + &siggen->data.base_config); + if (ret) + goto err; + + spipe->core_mask |= BIT(swidget->core); + + ret = sof_ipc4_widget_setup_msg(swidget, &siggen->msg); + if (ret) + goto err; + + return 0; +err: + sof_ipc4_free_audio_fmt(&siggen->available_fmt); + kfree(siggen); + swidget->private = NULL; + return ret; +} + +static void sof_ipc4_widget_free_comp_siggen(struct snd_sof_widget *swidget) +{ + struct sof_ipc4_siggen *siggen = swidget->private; + + if (!siggen) + return; + + sof_ipc4_free_audio_fmt(&siggen->available_fmt); + kfree(swidget->private); + swidget->private = NULL; +} + static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; @@ -1281,7 +1327,7 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget) static void sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct sof_ipc4_base_module_cfg *base_config) + struct sof_ipc4_base_module_cfg *base_config, int dir) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_widget *pipe_widget; @@ -1313,6 +1359,10 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * pipeline = pipe_widget->private; pipeline->mem_usage += total; + /* set pipeline direction */ + pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_DIRECTION_SET(0x1); + pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_DIRECTION(dir); + /* Update base_config->cpc from the module manifest */ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); @@ -2452,7 +2502,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config, dir); /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); @@ -2475,6 +2525,34 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return 0; } +static int sof_ipc4_prepare_siggen_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc4_siggen *siggen = swidget->private; + struct sof_ipc4_available_audio_format *available_fmt = &siggen->available_fmt; + struct sof_ipc4_audio_format *out_fmt; + + /* use the first available output format to set the base config audio format */ + if (available_fmt->num_output_formats != 1) { + dev_err(sdev->dev, "siggen should only have one available output format"); + return -EINVAL; + } + out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; + + /* copy output format */ + memcpy(&siggen->data.base_config.audio_fmt, out_fmt, sizeof(struct sof_ipc4_audio_format)); + siggen->data.base_config.obs = available_fmt->output_pin_fmts[0].buffer_size; + + /* update pipeline memory usage */ + sof_ipc4_update_resource_usage(sdev, swidget, &siggen->data.base_config, dir); + + return 0; +} + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, @@ -2515,7 +2593,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config, dir); return 0; } @@ -2560,7 +2638,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config, dir); return 0; } @@ -2626,7 +2704,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config, dir); out_audio_fmt = &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt; src->data.sink_rate = out_audio_fmt->sampling_frequency; @@ -2783,7 +2861,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config, dir); /* ipc_config_data is composed of the base_config followed by an optional extension */ memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg)); @@ -3158,6 +3236,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg = &asrc->msg; break; } + case snd_soc_dapm_siggen: + { + struct sof_ipc4_siggen *siggen = swidget->private; + + ipc_size = sizeof(siggen->data); + ipc_data = &siggen->data; + + msg = &siggen->msg; + break; + } case snd_soc_dapm_effect: { struct sof_ipc4_process *process = swidget->private; @@ -3848,6 +3936,13 @@ static enum sof_tokens mixer_token_list[] = { SOF_COMP_EXT_TOKENS, }; +static enum sof_tokens siggen_token_list[] = { + SOF_COMP_TOKENS, + SOF_AUDIO_FMT_NUM_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, + SOF_COMP_EXT_TOKENS, +}; + static enum sof_tokens src_token_list[] = { SOF_COMP_TOKENS, SOF_SRC_TOKENS, @@ -3920,6 +4015,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY process_token_list, ARRAY_SIZE(process_token_list), NULL, sof_ipc4_prepare_process_module, NULL}, + [snd_soc_dapm_siggen] = {sof_ipc4_widget_setup_comp_siggen, + sof_ipc4_widget_free_comp_siggen, + siggen_token_list, ARRAY_SIZE(siggen_token_list), NULL, + sof_ipc4_prepare_siggen_module, NULL}, }; const struct sof_ipc_tplg_ops ipc4_tplg_ops = { diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 9a028a59c5536a..eea32730a039bf 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -462,6 +462,27 @@ struct sof_ipc4_src { struct sof_ipc4_msg msg; }; +/* + * struct sof_ipc4_siggen_data - IPC data for siggen + * @base_config: IPC base config data + * @sink_rate: Output rate for sink module + */ +struct sof_ipc4_siggen_data { + struct sof_ipc4_base_module_cfg base_config; +} __packed __aligned(4); + +/** + * struct sof_ipc4_siggen - siggen config data + * @data: IPC base config data + * @available_fmt: Available audio format + * @msg: IPC4 message struct containing header and data info + */ +struct sof_ipc4_siggen { + struct sof_ipc4_siggen_data data; + struct sof_ipc4_available_audio_format available_fmt; + struct sof_ipc4_msg msg; +}; + /* * struct sof_ipc4_asrc_data - IPC data for ASRC * @base_config: IPC base config data diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 78fbe020e203a5..58a52fd5c8ccb6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -185,7 +185,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, } /* set the host DMA ID */ - if (tplg_ops && tplg_ops->host_config) + if (WIDGET_IS_AIF(host_widget->id) && tplg_ops && tplg_ops->host_config) tplg_ops->host_config(sdev, host_widget, platform_params); } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index afb0acb4e3dc4e..6bd3e99112600f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -13,6 +13,19 @@ #include "sof-audio.h" #include "ops.h" +/* + * Check is a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers + * starting with 0 and only the first DAI needs to be set up in the firmware. The rest of + * the aggregated DAI's are represented in the topology graph for completeness but do not + * need any firmware configuration. + */ +static bool is_aggregated_dai(struct snd_sof_widget *swidget) +{ + return (WIDGET_IS_DAI(swidget->id) && + isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) && + swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); +} + static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, const char *func) { @@ -412,6 +425,10 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg struct snd_soc_dapm_path *p; if (is_virtual_widget(sdev, widget, __func__)) + goto sink_unprepare; + + /* skip aggregated DAIs */ + if (!swidget || is_aggregated_dai(swidget)) return; /* skip if the widget is in use or if it is already unprepared */ @@ -452,12 +469,16 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget int ret; if (is_virtual_widget(sdev, widget, __func__)) - return 0; + goto sink_prepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; if (!widget_ops) return 0; + /* skip aggregated DAIs */ + if (!swidget || is_aggregated_dai(swidget)) + return 0; + if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) goto sink_prepare; @@ -506,18 +527,23 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_path *p; + struct snd_sof_widget *swidget; int err; int ret = 0; if (is_virtual_widget(sdev, widget, __func__)) - return 0; + goto sink_free; - if (widget->dobj.private) { - err = sof_widget_free(sdev, widget->dobj.private); - if (err < 0) - ret = err; - } + swidget = widget->dobj.private; + /* skip aggregated DAIs */ + if (!swidget || is_aggregated_dai(swidget)) + return 0; + + err = sof_widget_free(sdev, widget->dobj.private); + if (err < 0) + ret = err; +sink_free: /* free all widgets in the sink paths even in case of error to keep use counts balanced */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!p->walking) { @@ -552,11 +578,15 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d int ret; if (is_virtual_widget(sdev, widget, __func__)) - return 0; + goto sink_setup; if (swidget) { int i; + /* skip aggregated DAIs */ + if (!swidget || is_aggregated_dai(swidget)) + return 0; + ret = sof_widget_setup(sdev, widget->dobj.private); if (ret < 0) return ret; @@ -619,11 +649,10 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; for_each_dapm_widgets(list, i, widget) { - if (is_virtual_widget(sdev, widget, __func__)) - continue; - /* starting widget for playback is AIF type */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) + /* starting widget for playback is of AIF or snd_soc_dapm_input type */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK && (widget->id != snd_soc_dapm_aif_in && + widget->id != snd_soc_dapm_input)) continue; /* starting widget for capture is DAI type */ @@ -904,29 +933,41 @@ struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, return NULL; } -/* find widget by stream name and direction */ -struct snd_sof_widget * -snd_sof_find_swidget_sname(struct snd_soc_component *scomp, - const char *pcm_name, int dir) +static struct snd_sof_widget *snd_sof_find_swidget_sname_type(struct snd_soc_component *scomp, + const char *sname, + enum snd_soc_dapm_type type) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget; - enum snd_soc_dapm_type type; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - type = snd_soc_dapm_aif_in; - else - type = snd_soc_dapm_aif_out; list_for_each_entry(swidget, &sdev->widget_list, list) { - if (!strcmp(pcm_name, swidget->widget->sname) && - swidget->id == type) + if (!strcmp(sname, swidget->widget->sname) && swidget->id == type) return swidget; } return NULL; } +/* find widget by stream name and direction */ +struct snd_sof_widget * +snd_sof_find_swidget_sname(struct snd_soc_component *scomp, + const char *pcm_name, int dir) +{ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + struct snd_sof_widget *swidget; + + /* first look for an aif_in type widget */ + swidget = snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_aif_in); + if (swidget) + return swidget; + + /* if not found, look for an input type widget */ + return snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_input); + } + + return snd_sof_find_swidget_sname_type(scomp, pcm_name, snd_soc_dapm_aif_out); +} + struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, const char *name) {