Unverified Commit c04c2e82 authored by Seppo Ingalsuo's avatar Seppo Ingalsuo Committed by Mark Brown
Browse files

ASoC: SOF: ipc4-topology: Add support for 8-bit formats



This patch enables use of 8-bit unsigned, A-law, and mu-law
sample formats with IPC4 SOF.

The ipc4-topology.h is updated with IPC4 sample types. The
purpose is to convert ALSA types to IPC4 types.

The functions of_ipc4_update_hw_params(), sof_ipc4_get_valid_bits(),
and new function sof_ipc4_get_sample_type() are updated to handle
the sample type conversions.

The function sof_ipc4_prepare_copier_module() is updated to set
the DMA SCS bit for all non 32 bits sample types.

The change to function sof_ipc4_get_valid_bits() returns 8 bits
for these ALSA formats.

The change to function sof_ipc4_prepare_copier_module() is
needed to handle properly all non 32-bit formats with SCS
bit set.

To support playback with new 8 bits types, the
sof_ipc4_init_input_audio_fmt() function is updated to get
the sample type and use it in search for copier input pin
format.

To support capture, the sof_ipc4_init_output_audio_fmt()
is updated similarly. Since the function uses separate
out_ref_type argument, instead of single parameters struct,
the out_ref_type needs to be added to every user of the
function. Therefore functions sof_ipc4_prepare_copier_module(),
sof_ipc4_prepare_gain_module(), sof_ipc4_prepare_mixer_module(),
sof_ipc4_prepare_src_module(), and sof_ipc4_prepare_process_module()
are updated to set the out_ref_type.

Signed-off-by: default avatarSeppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarLiam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Message-ID: <20250829105305.31818-3-peter.ujfalusi@linux.intel.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6ad299a9
Loading
Loading
Loading
Loading
+107 −19
Original line number Diff line number Diff line
@@ -1292,6 +1292,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)
@@ -1300,10 +1317,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;
@@ -1375,7 +1409,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;
@@ -1401,19 +1435,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;

@@ -1426,6 +1463,10 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
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:
@@ -1438,6 +1479,26 @@ static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_p
	}
}

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;
	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,
@@ -1449,8 +1510,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) {
@@ -1466,6 +1529,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
@@ -1479,14 +1546,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;
	}

@@ -1882,7 +1952,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;
@@ -1923,10 +1993,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.
@@ -2051,6 +2124,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 =
@@ -2061,6 +2135,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)
@@ -2085,12 +2163,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;

@@ -2319,7 +2399,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,
@@ -2333,13 +2413,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;

@@ -2362,7 +2444,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,
@@ -2376,13 +2458,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;

@@ -2406,7 +2490,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,
@@ -2433,6 +2517,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
@@ -2446,7 +2531,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;

@@ -2570,20 +2656,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;

+9 −0
Original line number Diff line number Diff line
@@ -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)