Commit fc1d62b3 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: ipc4-topology: Improve the audio format

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

Improve the logic to account for the current restrictions in topology while
making it extensible for future topology modifications.

The current topology definitions assume that input/output formats come in pairs.
For example even if there's only 1 output format for a module, we add 3 output
formats to match that of the input format count with the same parameters.
This is unnecessary but we have to deal with it until the topologies are
modified. Additionally, choosing the input/output audio format should
depend only on the pipeline params or the runtime FE hw_params depending
on where the module is in the pipeline.

This series modifies the logic for selection based on this and removes
unnecessary dependencies between the input and output formats.
parents 25428a9d 5a56c533
......@@ -1028,47 +1028,125 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
return 0;
}
static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config,
struct snd_pcm_hw_params *params,
struct sof_ipc4_available_audio_format *available_fmt,
struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev,
struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
{
u32 valid_bits;
u32 channels;
u32 rate;
int sample_valid_bits;
struct sof_ipc4_audio_format *fmt;
u32 rate, channels, valid_bits;
int i;
fmt = &pin_fmts[0].audio_fmt;
rate = fmt->sampling_frequency;
channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
/* check if all output formats in topology are the same */
for (i = 1; i < pin_fmts_size; i++) {
u32 _rate, _channels, _valid_bits;
fmt = &pin_fmts[i].audio_fmt;
_rate = fmt->sampling_frequency;
_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
if (_rate != rate || _channels != channels || _valid_bits != valid_bits)
return false;
}
return true;
}
static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
struct sof_ipc4_base_module_cfg *base_config,
struct sof_ipc4_available_audio_format *available_fmt,
u32 out_ref_rate, u32 out_ref_channels,
u32 out_ref_valid_bits)
{
struct sof_ipc4_audio_format *out_fmt;
bool single_format;
int i;
if (!pin_fmts) {
dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name);
if (!available_fmt->num_output_formats)
return -EINVAL;
single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts,
available_fmt->num_output_formats);
/* pick the first format if there's only one available or if all formats are the same */
if (single_format) {
base_config->obs = available_fmt->output_pin_fmts[0].buffer_size;
return 0;
}
/*
* if there are multiple output formats, then choose the output format that matches
* the reference params
*/
for (i = 0; i < available_fmt->num_output_formats; i++) {
u32 _out_rate, _out_channels, _out_valid_bits;
out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
_out_rate = out_fmt->sampling_frequency;
_out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg);
_out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
_out_valid_bits == out_ref_valid_bits) {
base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
return i;
}
}
return -EINVAL;
}
static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
{
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
sample_valid_bits = 16;
break;
return 16;
case SNDRV_PCM_FORMAT_S24_LE:
sample_valid_bits = 24;
break;
return 24;
case SNDRV_PCM_FORMAT_S32_LE:
sample_valid_bits = 32;
break;
return 32;
default:
dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
return -EINVAL;
}
}
if (!pin_fmts_size) {
dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name);
static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config,
struct snd_pcm_hw_params *params,
struct sof_ipc4_available_audio_format *available_fmt)
{
struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts;
u32 pin_fmts_size = available_fmt->num_input_formats;
u32 valid_bits;
u32 channels;
u32 rate;
bool single_format;
int sample_valid_bits;
int i = 0;
if (!available_fmt->num_input_formats) {
dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name);
return -EINVAL;
}
single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
if (single_format)
goto in_fmt;
sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params);
if (sample_valid_bits < 0)
return sample_valid_bits;
/*
* Search supported audio formats with pin index 0 to match rate, channels ,and
* sample_valid_bytes from runtime params
* Search supported input audio formats with pin index 0 to match rate, channels and
* sample_valid_bits from reference params
*/
for (i = 0; i < pin_fmts_size; i++) {
struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
......@@ -1093,6 +1171,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
return -EINVAL;
}
in_fmt:
/* copy input format */
if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {
memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,
......@@ -1105,10 +1184,6 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);
}
if (available_fmt->num_output_formats && i < available_fmt->num_output_formats)
base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
/* Return the index of the matched format */
return i;
}
......@@ -1288,50 +1363,6 @@ static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth)
return 0;
}
static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev,
struct snd_pcm_hw_params *pipeline_params,
struct snd_pcm_hw_params *fe_params,
struct sof_ipc4_available_audio_format *available_fmt)
{
struct sof_ipc4_audio_format *audio_fmt;
unsigned int sample_valid_bits;
bool multiple_formats = false;
bool fe_format_match = false;
struct snd_mask *fmt;
int i;
for (i = 0; i < available_fmt->num_output_formats; i++) {
unsigned int val;
audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg);
if (i == 0)
sample_valid_bits = val;
else if (sample_valid_bits != val)
multiple_formats = true;
if (snd_pcm_format_width(params_format(fe_params)) == val)
fe_format_match = true;
}
fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(fmt);
if (multiple_formats) {
if (fe_format_match) {
/* multiple formats defined and one matches FE */
snd_mask_set_format(fmt, params_format(fe_params));
return 0;
}
dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n");
return -EINVAL;
}
return ipc4_set_fmt_mask(fmt, sample_valid_bits);
}
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
......@@ -1341,7 +1372,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_pin_format *format_list_to_search;
struct sof_ipc4_copier_data *copier_data;
struct snd_pcm_hw_params *ref_params;
struct sof_ipc4_copier *ipc4_copier;
......@@ -1352,8 +1382,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
int *ipc_config_size;
u32 **data;
int ipc_size, ret;
u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
u32 deep_buffer_dma_ms = 0;
u32 format_list_count;
int output_fmt_index;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
......@@ -1417,13 +1448,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
* for capture.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
format_list_to_search = available_fmt->input_pin_fmts;
format_list_count = available_fmt->num_input_formats;
} else {
format_list_to_search = available_fmt->output_pin_fmts;
format_list_count = available_fmt->num_output_formats;
}
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ref_params = fe_params;
else
ref_params = pipeline_params;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |=
......@@ -1431,7 +1459,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
/* set gateway attributes */
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
ref_params = fe_params;
break;
}
case snd_soc_dapm_dai_in:
......@@ -1448,20 +1475,17 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)dai->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
if (dir == SNDRV_PCM_STREAM_CAPTURE) {
format_list_to_search = available_fmt->output_pin_fmts;
format_list_count = available_fmt->num_output_formats;
ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params,
available_fmt);
if (ret < 0)
return ret;
} else {
format_list_to_search = available_fmt->input_pin_fmts;
format_list_count = available_fmt->num_input_formats;
}
ref_params = pipeline_params;
/*
* When there is format conversion within a pipeline, the number of supported
* output formats is typically limited to just 1 for the DAI copiers. But when there
* is no format conversion, the DAI copiers input format must match that of the
* FE hw_params for capture and the pipeline params for playback.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ref_params = pipeline_params;
else
ref_params = fe_params;
ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
ipc4_copier->dai_type, dir,
......@@ -1477,10 +1501,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
/* Use the input formats to match pcm params */
format_list_to_search = available_fmt->input_pin_fmts;
format_list_count = available_fmt->num_input_formats;
ref_params = pipeline_params;
break;
......@@ -1492,11 +1512,51 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* set input and output audio formats */
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
available_fmt, format_list_to_search, format_list_count);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
available_fmt);
if (ret < 0)
return ret;
/* set the reference params for output format selection */
switch (swidget->id) {
case snd_soc_dapm_aif_in:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_buffer:
{
struct sof_ipc4_audio_format *in_fmt;
in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
break;
}
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai_in:
out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
if (out_ref_valid_bits < 0)
return out_ref_valid_bits;
out_ref_rate = params_rate(fe_params);
out_ref_channels = params_channels(fe_params);
break;
default:
/*
* Unsupported type should be caught by the former switch default
* case, this should never happen in reality.
*/
return -EINVAL;
}
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config,
available_fmt, out_ref_rate,
out_ref_channels, out_ref_valid_bits);
if (output_fmt_index < 0) {
dev_err(sdev->dev, "Failed to initialize output format for %s",
swidget->widget->name);
return output_fmt_index;
}
/*
* Set the output format. Current topology defines pin 0 input and output formats in pairs.
* This assumes that the pin 0 formats are defined before all other pins.
......@@ -1504,10 +1564,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* input format. This logic will need to be updated when the format definitions
* in topology change.
*/
memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
memcpy(&copier_data->out_format,
&available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
sizeof(struct sof_ipc4_audio_format));
dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);
sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1);
sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1);
switch (swidget->id) {
case snd_soc_dapm_dai_in:
......@@ -1515,7 +1576,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
{
/*
* Only SOF_DAI_INTEL_ALH needs copier_data to set blob.
* That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt
* That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt
*/
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
......@@ -1664,15 +1725,28 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_gain *gain = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
int ret;
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
pipeline_params, available_fmt,
available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->base_config,
pipeline_params, available_fmt);
if (ret < 0)
return ret;
in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt,
out_ref_rate, out_ref_channels, out_ref_valid_bits);
if (ret < 0) {
dev_err(sdev->dev, "Failed to initialize output format for %s",
swidget->widget->name);
return ret;
}
/* update pipeline memory usage */
sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config);
......@@ -1688,15 +1762,28 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_mixer *mixer = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
int ret;
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
pipeline_params, available_fmt,
available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config,
pipeline_params, available_fmt);
if (ret < 0)
return ret;
in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt,
out_ref_rate, out_ref_channels, out_ref_valid_bits);
if (ret < 0) {
dev_err(sdev->dev, "Failed to initialize output format for %s",
swidget->widget->name);
return ret;
}
/* update pipeline memory usage */
sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config);
......@@ -1712,16 +1799,28 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_src *src = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
struct snd_interval *rate;
int ret;
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
pipeline_params, available_fmt,
available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config,
pipeline_params, available_fmt);
if (ret < 0)
return ret;
in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt,
out_ref_rate, out_ref_channels, out_ref_valid_bits);
if (ret < 0) {
dev_err(sdev->dev, "Failed to initialize output format for %s",
swidget->widget->name);
}
/* update pipeline memory usage */
sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config);
......@@ -1820,20 +1919,37 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_process *process = swidget->private;
struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
struct sof_ipc4_audio_format *in_fmt;
u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
void *cfg = process->ipc_config_data;
int output_fmt_index;
int ret;
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config,
pipeline_params, available_fmt,
available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config,
pipeline_params, available_fmt);
if (ret < 0)
return ret;
in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config,
available_fmt, out_ref_rate,
out_ref_channels, out_ref_valid_bits);
if (output_fmt_index < 0 && available_fmt->num_output_formats) {
dev_err(sdev->dev, "Failed to initialize output format for %s",
swidget->widget->name);
return output_fmt_index;
}
/* copy Pin 0 output format */
if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats &&
!available_fmt->output_pin_fmts[ret].pin_index) {
memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
if (available_fmt->num_output_formats &&
output_fmt_index < available_fmt->num_output_formats &&
!available_fmt->output_pin_fmts[output_fmt_index].pin_index) {
memcpy(&process->output_format,
&available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
sizeof(struct sof_ipc4_audio_format));
/* modify the pipeline params with the pin 0 output format */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment