diff options
| -rw-r--r-- | sound/soc/sof/ipc4-pcm.c | 56 | ||||
| -rw-r--r-- | sound/soc/sof/ipc4-topology.c | 142 | ||||
| -rw-r--r-- | sound/soc/sof/ipc4-topology.h | 9 |
3 files changed, 187 insertions, 20 deletions
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 374dc10d10fd..cb9a06792a47 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -727,6 +727,58 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, return 0; } +static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev, + struct snd_pcm_hw_params *params, + struct sof_ipc4_copier *ipc4_copier) +{ + struct sof_ipc4_pin_format *pin_fmts = ipc4_copier->available_fmt.input_pin_fmts; + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int num_input_formats = ipc4_copier->available_fmt.num_input_formats; + unsigned int fe_channels = params_channels(params); + bool fe_be_match = false; + bool single_be_channels = true; + unsigned int be_channels, val; + int i; + + if (WARN_ON_ONCE(!num_input_formats)) + return -EINVAL; + + /* + * Copier does not change channels, so we + * need to only consider the input pin information. + */ + be_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[0].audio_fmt.fmt_cfg); + for (i = 0; i < num_input_formats; i++) { + val = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(pin_fmts[i].audio_fmt.fmt_cfg); + + if (val != be_channels) + single_be_channels = false; + + if (val == fe_channels) { + fe_be_match = true; + break; + } + } + + /* + * If channels is different than FE channels, topology must contain a + * module which can change the number of channels. But we do require + * topology to define a single channels in the DAI copier config in + * this case (FE channels may be variable). + */ + if (!fe_be_match) { + if (!single_be_channels) { + dev_err(sdev->dev, "Unable to select channels for DAI link\n"); + return -EINVAL; + } + + channels->min = be_channels; + channels->max = be_channels; + } + + return 0; +} + static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -790,6 +842,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, if (ret) return ret; + ret = sof_ipc4_pcm_dai_link_fixup_channels(sdev, params, ipc4_copier); + if (ret) + return ret; + if (single_bitdepth) { snd_mask_none(fmt); valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(ipc4_fmt->fmt_cfg); diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 74a1319d4bd2..f5e62cd8fc0c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1334,6 +1334,23 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, return 0; } +static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg) +{ + /* Fetch the sample type from the fmt for 8 and 32 bit formats */ + u32 __bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt_cfg); + + if (__bits == 8 || __bits == 32) + return SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(fmt_cfg); + + /* + * Return LSB integer type for 20 and 24 formats as the firmware is + * handling the LSB/MSB alignment internally, for the kernel this + * should not be taken into account, we treat them as LSB to match with + * the format we support on the PCM side. + */ + return SOF_IPC4_TYPE_LSB_INTEGER; +} + /* update hw_params based on the audio stream format */ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params, struct sof_ipc4_audio_format *fmt, u32 param_to_update) @@ -1342,10 +1359,27 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + int type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg); snd_pcm_format_t snd_fmt; struct snd_mask *m; switch (valid_bits) { + case 8: + switch (type) { + case SOF_IPC4_TYPE_A_LAW: + snd_fmt = SNDRV_PCM_FORMAT_A_LAW; + break; + case SOF_IPC4_TYPE_MU_LAW: + snd_fmt = SNDRV_PCM_FORMAT_MU_LAW; + break; + case SOF_IPC4_TYPE_UNSIGNED_INTEGER: + snd_fmt = SNDRV_PCM_FORMAT_U8; + break; + default: + dev_err(sdev->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type); + return -EINVAL; + } + break; case 16: snd_fmt = SNDRV_PCM_FORMAT_S16_LE; break; @@ -1353,7 +1387,17 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw snd_fmt = SNDRV_PCM_FORMAT_S24_LE; break; case 32: - snd_fmt = SNDRV_PCM_FORMAT_S32_LE; + switch (type) { + case SOF_IPC4_TYPE_LSB_INTEGER: + snd_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; + case SOF_IPC4_TYPE_FLOAT: + snd_fmt = SNDRV_PCM_FORMAT_FLOAT_LE; + break; + default: + dev_err(sdev->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type); + return -EINVAL; + } break; default: dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits); @@ -1417,7 +1461,7 @@ 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) + u32 out_ref_valid_bits, u32 out_ref_type) { struct sof_ipc4_pin_format *pin_fmts = available_fmt->output_pin_fmts; u32 pin_fmts_size = available_fmt->num_output_formats; @@ -1443,19 +1487,22 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, for (i = 0; i < pin_fmts_size; i++) { struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt; - u32 _out_rate, _out_channels, _out_valid_bits; + u32 _out_rate, _out_channels, _out_valid_bits, _out_type; _out_rate = fmt->sampling_frequency; _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + _out_type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg); if (_out_rate == out_ref_rate && _out_channels == out_ref_channels && - _out_valid_bits == out_ref_valid_bits) + _out_valid_bits == out_ref_valid_bits && _out_type == out_ref_type) goto out_fmt; } - dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n", - __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels); + dev_err(sdev->dev, + "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", + __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels, + out_ref_type); return -EINVAL; @@ -1468,18 +1515,46 @@ out_fmt: 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_U8: + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; case SNDRV_PCM_FORMAT_S16_LE: return 16; case SNDRV_PCM_FORMAT_S24_LE: return 24; case SNDRV_PCM_FORMAT_S32_LE: return 32; + case SNDRV_PCM_FORMAT_FLOAT_LE: + return 32; default: dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); return -EINVAL; } } +static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +{ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_A_LAW: + return SOF_IPC4_TYPE_A_LAW; + case SNDRV_PCM_FORMAT_MU_LAW: + return SOF_IPC4_TYPE_MU_LAW; + case SNDRV_PCM_FORMAT_U8: + return SOF_IPC4_TYPE_UNSIGNED_INTEGER; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + return SOF_IPC4_TYPE_LSB_INTEGER; + case SNDRV_PCM_FORMAT_FLOAT_LE: + return SOF_IPC4_TYPE_FLOAT; + default: + dev_err(sdev->dev, "invalid pcm sample type %d\n", params_format(params)); + return -EINVAL; + } +} + 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, @@ -1491,8 +1566,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, u32 valid_bits; u32 channels; u32 rate; + u32 type; bool single_format; int sample_valid_bits; + int sample_type; int i = 0; if (!pin_fmts_size) { @@ -1508,6 +1585,10 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, if (sample_valid_bits < 0) return sample_valid_bits; + sample_type = sof_ipc4_get_sample_type(sdev, params); + if (sample_type < 0) + return sample_type; + /* * Search supported input audio formats with pin index 0 to match rate, channels and * sample_valid_bits from reference params @@ -1521,14 +1602,17 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, 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); + type = sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg); if (params_rate(params) == rate && params_channels(params) == channels && - sample_valid_bits == valid_bits) + sample_valid_bits == valid_bits && sample_type == type) break; } if (i == pin_fmts_size) { - dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n", - __func__, params_rate(params), sample_valid_bits, params_channels(params)); + dev_err(sdev->dev, + "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", + __func__, params_rate(params), sample_valid_bits, + params_channels(params), sample_type); return -EINVAL; } @@ -1924,7 +2008,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, int *ipc_config_size; u32 **data; int ipc_size, ret, out_ref_valid_bits; - u32 out_ref_rate, out_ref_channels; + u32 out_ref_rate, out_ref_channels, out_ref_type; u32 deep_buffer_dma_ms = 0; bool single_output_bitdepth; int i; @@ -1965,10 +2049,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, host_dma_id = platform_params->stream_tag - 1; pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); - /* Set SCS bit for S16_LE format only */ if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE) pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; + /* Set SCS bit for 8 and 16 bit formats */ + if (params_physical_width(fe_params) <= 16) + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; + /* * Despite its name the bitfield 'fifo_size' is used to define DMA buffer * size. The expression calculates 2ms buffer size. @@ -2093,6 +2180,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].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_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); if (!single_output_bitdepth) out_ref_valid_bits = @@ -2103,6 +2191,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, case snd_soc_dapm_dai_in: out_ref_rate = params_rate(fe_params); out_ref_channels = params_channels(fe_params); + out_ref_type = sof_ipc4_get_sample_type(sdev, fe_params); + if (out_ref_type < 0) + return out_ref_type; + if (!single_output_bitdepth) { out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); if (out_ref_valid_bits < 0) @@ -2127,12 +2219,14 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg); + out_ref_type = sof_ipc4_fmt_cfg_to_type(out_fmt->fmt_cfg); } output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &copier_data->base_config, available_fmt, out_ref_rate, - out_ref_channels, out_ref_valid_bits); + out_ref_channels, out_ref_valid_bits, + out_ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2361,7 +2455,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, 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; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, @@ -2375,13 +2469,15 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, 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); + out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &gain->data.base_config, available_fmt, out_ref_rate, out_ref_channels, - out_ref_valid_bits); + out_ref_valid_bits, + out_ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2404,7 +2500,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, 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; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int input_fmt_index, output_fmt_index; input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, @@ -2418,13 +2514,15 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, 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); + out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &mixer->base_config, available_fmt, out_ref_rate, out_ref_channels, - out_ref_valid_bits); + out_ref_valid_bits, + out_ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2448,7 +2546,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; struct sof_ipc4_audio_format *out_audio_fmt; struct sof_ipc4_audio_format *in_audio_fmt; - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; int output_fmt_index, input_fmt_index; input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, @@ -2475,6 +2573,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, in_audio_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg); out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg); + out_ref_type = sof_ipc4_fmt_cfg_to_type(in_audio_fmt->fmt_cfg); /* * For capture, the SRC module should convert the rate to match the rate requested by the @@ -2488,7 +2587,8 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, available_fmt, out_ref_rate, out_ref_channels, - out_ref_valid_bits); + out_ref_valid_bits, + out_ref_type); if (output_fmt_index < 0) return output_fmt_index; @@ -2612,20 +2712,22 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, struct sof_ipc4_audio_format *in_fmt; struct sof_ipc4_pin_format *pin_fmt; u32 out_ref_rate, out_ref_channels; - int out_ref_valid_bits; + int out_ref_valid_bits, out_ref_type; in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].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); + out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, &process->base_config, available_fmt, out_ref_rate, out_ref_channels, - out_ref_valid_bits); + out_ref_valid_bits, + out_ref_type); if (output_fmt_index < 0) return output_fmt_index; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index e8e848233314..dfa1a6c2ffa8 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -41,6 +41,15 @@ #define SOF_IPC4_FW_MAX_PAGE_COUNT 20 #define SOF_IPC4_FW_MAX_QUEUE_COUNT 8 +/* IPC4 sample types */ +#define SOF_IPC4_TYPE_MSB_INTEGER 0 +#define SOF_IPC4_TYPE_LSB_INTEGER 1 +#define SOF_IPC4_TYPE_SIGNED_INTEGER 2 +#define SOF_IPC4_TYPE_UNSIGNED_INTEGER 3 +#define SOF_IPC4_TYPE_FLOAT 4 +#define SOF_IPC4_TYPE_A_LAW 5 +#define SOF_IPC4_TYPE_MU_LAW 6 + /* Node index and mask applicable for host copier and ALH/HDA type DAI copiers */ #define SOF_IPC4_NODE_INDEX_MASK 0xFF #define SOF_IPC4_NODE_INDEX(x) ((x) & SOF_IPC4_NODE_INDEX_MASK) |
