Unverified Commit f283219b authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: Intel: HDA/DMIC updates

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

The first patch handles a problematic configuration where the wrong
machine driver/topology is used: when the hardware reports an external
HDaudio codec the direction is to ignore/discard ACPI SoundWire
devices.

The last two patch deal with DMIC format configurations and allow
users to select S16_LE even if the DMIC and internal copiers only
support 24 or 32-bits. The code changes are located in sound/soc/sof/
but in the scope of Intel DAIs.
parents 1ae14f35 f9209644
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -1809,6 +1809,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
	u32 interface_mask = hda_get_interface_mask(sdev);
	struct snd_sof_pdata *sof_pdata = sdev->pdata;
	const struct sof_dev_desc *desc = sof_pdata->desc;
	struct hdac_bus *bus = sof_to_bus(sdev);
	struct snd_soc_acpi_mach *mach = NULL;
	enum snd_soc_acpi_intel_codec codec_type;
	const char *tplg_filename;
@@ -1981,8 +1982,12 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
		}
	}

	/* If I2S fails, try SoundWire if it is supported */
	if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
	/*
	 * If I2S fails and no external HDaudio codec is detected,
	 * try SoundWire if it is supported
	 */
	if (!mach && !HDA_EXT_CODEC(bus->codec_mask) &&
	    (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
		mach = hda_sdw_machine_select(sdev);

	/*
+2 −0
Original line number Diff line number Diff line
@@ -454,6 +454,8 @@
#define SSP_SET_SFRM_CONSUMER	BIT(24)
#define SSP_SET_CBP_CFP		(SSP_SET_SCLK_CONSUMER | SSP_SET_SFRM_CONSUMER)

#define HDA_EXT_ADDR		0
#define HDA_EXT_CODEC(x) ((x) & BIT(HDA_EXT_ADDR))
#define HDA_IDISP_ADDR		2
#define HDA_IDISP_CODEC(x) ((x) & BIT(HDA_IDISP_ADDR))

+187 −58
Original line number Diff line number Diff line
@@ -1119,13 +1119,14 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,

/* 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)
				     struct sof_ipc4_audio_format *fmt, u32 param_to_update)
{
	snd_pcm_format_t snd_fmt;
	struct snd_interval *i;
	struct snd_mask *m;

	if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
		int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
	unsigned int channels, rate;
		snd_pcm_format_t snd_fmt;
		struct snd_mask *m;

		switch (valid_bits) {
		case 16:
@@ -1145,16 +1146,23 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
		m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
		snd_mask_none(m);
		snd_mask_set_format(m, snd_fmt);
	}

	if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_RATE)) {
		unsigned int rate = fmt->sampling_frequency;

	rate = fmt->sampling_frequency;
		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
		i->min = rate;
		i->max = rate;
	}

	if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {
		unsigned int channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);

	channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
		i->min = channels;
		i->max = channels;
	}

	return 0;
}
@@ -1412,13 +1420,16 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof
	return 0;
}

static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
static int
snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
			       bool single_format,
			       struct snd_pcm_hw_params *params, u32 dai_index,
			       u32 linktype, u8 dir, u32 **dst, u32 *len)
{
	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
	struct nhlt_specific_cfg *cfg;
	int sample_rate, channel_count;
	bool format_change = false;
	int bit_depth, ret;
	u32 nhlt_type;
	int dev_type = 0;
@@ -1427,9 +1438,18 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
	switch (linktype) {
	case SOF_DAI_INTEL_DMIC:
		nhlt_type = NHLT_LINK_DMIC;
		bit_depth = params_width(params);
		channel_count = params_channels(params);
		sample_rate = params_rate(params);
		bit_depth = params_width(params);
		/*
		 * Look for 32-bit blob first instead of 16-bit if copier
		 * supports multiple formats
		 */
		if (bit_depth == 16 && !single_format) {
			dev_dbg(sdev->dev, "Looking for 32-bit blob first for DMIC\n");
			format_change = true;
			bit_depth = 32;
		}
		break;
	case SOF_DAI_INTEL_SSP:
		nhlt_type = NHLT_LINK_SSP;
@@ -1463,20 +1483,54 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
					   dir, dev_type);

	if (!cfg) {
		if (format_change) {
			/*
			 * The 32-bit blob was not found in NHLT table, try to
			 * look for one based on the params
			 */
			bit_depth = params_width(params);
			format_change = false;

			cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,
							   dai_index, nhlt_type,
							   bit_depth, bit_depth,
							   channel_count, sample_rate,
							   dir, dev_type);
			if (cfg)
				goto out;
		}

		dev_err(sdev->dev,
			"no matching blob for sample rate: %d sample width: %d channels: %d\n",
			sample_rate, bit_depth, channel_count);
		return -EINVAL;
	}

out:
	/* config length should be in dwords */
	*len = cfg->size >> 2;
	*dst = (u32 *)cfg->caps;

	if (format_change) {
		/*
		 * Update the params to reflect that we have loaded 32-bit blob
		 * instead of the 16-bit.
		 * This information is going to be used by the caller to find
		 * matching copier format on the dai side.
		 */
		struct snd_mask *m;

		m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
		snd_mask_none(m);
		snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE);
	}

	return 0;
}
#else
static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
static int
snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
			       bool single_format,
			       struct snd_pcm_hw_params *params, u32 dai_index,
			       u32 linktype, u8 dir, u32 **dst, u32 *len)
{
@@ -1509,6 +1563,68 @@ bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
	return true;
}

static int
sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
			    struct snd_pcm_hw_params *params, int dir)
{
	struct sof_ipc4_available_audio_format *available_fmt;
	struct snd_pcm_hw_params dai_params = *params;
	struct sof_ipc4_copier_data *copier_data;
	struct sof_ipc4_copier *ipc4_copier;
	bool single_format;
	int ret;

	ipc4_copier = dai->private;
	copier_data = &ipc4_copier->data;
	available_fmt = &ipc4_copier->available_fmt;

	/*
	 * If the copier on the DAI side supports only single bit depth then
	 * this depth (format) should be used to look for the NHLT blob (if
	 * needed) and in case of capture this should be used for the input
	 * format lookup
	 */
	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
		single_format = sof_ipc4_copier_is_single_format(sdev,
						available_fmt->output_pin_fmts,
						available_fmt->num_output_formats);

		/* Update the dai_params with the only supported format */
		if (single_format) {
			ret = sof_ipc4_update_hw_params(sdev, &dai_params,
					&available_fmt->output_pin_fmts[0].audio_fmt,
					BIT(SNDRV_PCM_HW_PARAM_FORMAT));
			if (ret)
				return ret;
		}
	} else {
		single_format = sof_ipc4_copier_is_single_format(sdev,
						available_fmt->input_pin_fmts,
						available_fmt->num_input_formats);

		/* Update the dai_params with the only supported format */
		if (single_format) {
			ret = sof_ipc4_update_hw_params(sdev, &dai_params,
					&available_fmt->input_pin_fmts[0].audio_fmt,
					BIT(SNDRV_PCM_HW_PARAM_FORMAT));
			if (ret)
				return ret;
		}
	}

	ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_format,
					     &dai_params,
					     ipc4_copier->dai_index,
					     ipc4_copier->dai_type, dir,
					     &ipc4_copier->copier_config,
					     &copier_data->gtw_cfg.config_length);
	/* Update the params to reflect the changes made in this function */
	if (!ret)
		*params = dai_params;

	return ret;
}

static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
			       struct snd_pcm_hw_params *fe_params,
@@ -1519,7 +1635,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
	struct snd_soc_component *scomp = swidget->scomp;
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct sof_ipc4_copier_data *copier_data;
	struct snd_pcm_hw_params *ref_params;
	struct snd_pcm_hw_params ref_params;
	struct sof_ipc4_copier *ipc4_copier;
	struct snd_sof_dai *dai;
	u32 gtw_cfg_config_length;
@@ -1597,9 +1713,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
		 * for capture.
		 */
		if (dir == SNDRV_PCM_STREAM_PLAYBACK)
			ref_params = fe_params;
			ref_params = *fe_params;
		else
			ref_params = pipeline_params;
			ref_params = *pipeline_params;

		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
		copier_data->gtw_cfg.node_id |=
@@ -1625,23 +1741,25 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
		available_fmt = &ipc4_copier->available_fmt;

		/*
		 * 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.
		 * Use the fe_params as a base for the copier configuration.
		 * The ref_params might get updated to reflect what format is
		 * supported by the copier on the DAI side.
		 *
		 * In case of capture the ref_params returned will be used to
		 * find the input configuration of the copier.
		 */
		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,
						     &ipc4_copier->copier_config,
						     &copier_data->gtw_cfg.config_length);
		ref_params = *fe_params;
		ret = sof_ipc4_prepare_dai_copier(sdev, dai, &ref_params, dir);
		if (ret < 0)
			return ret;

		/*
		 * For playback the pipeline_params needs to be used to find the
		 * input configuration of the copier.
		 */
		if (dir == SNDRV_PCM_STREAM_PLAYBACK)
			ref_params = *pipeline_params;

		break;
	}
	case snd_soc_dapm_buffer:
@@ -1649,7 +1767,7 @@ 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;
		ref_params = pipeline_params;
		ref_params = *pipeline_params;

		break;
	}
@@ -1660,8 +1778,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
	}

	/* set input and output audio formats */
	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
					    available_fmt);
	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config,
					    &ref_params, available_fmt);
	if (ret < 0)
		return ret;

@@ -1844,7 +1962,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
	}

	/* modify the input params for the next widget */
	ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format);
	ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
					&copier_data->out_format,
					BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
					BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
					BIT(SNDRV_PCM_HW_PARAM_RATE));
	if (ret)
		return ret;

@@ -2069,7 +2191,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
	src->data.sink_rate = out_audio_fmt->sampling_frequency;

	/* update pipeline_params for sink widgets */
	return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt);
	return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt,
					 BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
					 BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
					 BIT(SNDRV_PCM_HW_PARAM_RATE));
}

static int
@@ -2193,7 +2318,11 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
		       sizeof(struct sof_ipc4_audio_format));

		/* modify the pipeline params with the pin 0 output format */
		ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
		ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
						&process->output_format,
						BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
						BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
						BIT(SNDRV_PCM_HW_PARAM_RATE));
		if (ret)
			return ret;
	}