Unverified Commit 750e1a22 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: core/Intel: Introduce DSPless mode

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

This series will add support for SOF Linux stack to run without using the DSP.

DSPless mode provides a good tool for verification that the hardware itself
works correctly by taking the DSP use out from the picture.
It can only work with interfaces which supports this mode: Intel HDA at the
moment but with LNL it could be possible to support other audio interfaces.

The main driver for this mode is to be able to test programming sequences,
low-level code and for low-level verification of a platform.

The feature is not targetted for end-users and it will not make the SOF stack
to work on hardware without DSP, but it is giving us a tool to debug and enable
platforms earlier (when for example t he firmware is not mature enough).
parents 194f8692 5962c2a5
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct snd_sof_dev;
/**
 * enum sof_fw_state - DSP firmware state definitions
 * @SOF_FW_BOOT_NOT_STARTED:	firmware boot is not yet started
 * @SOF_DSPLESS_MODE:		DSP is not used
 * @SOF_FW_BOOT_PREPARE:	preparing for boot (firmware loading for exaqmple)
 * @SOF_FW_BOOT_IN_PROGRESS:	firmware boot is in progress
 * @SOF_FW_BOOT_FAILED:		firmware boot failed
@@ -31,6 +32,7 @@ struct snd_sof_dev;
 */
enum sof_fw_state {
	SOF_FW_BOOT_NOT_STARTED = 0,
	SOF_DSPLESS_MODE,
	SOF_FW_BOOT_PREPARE,
	SOF_FW_BOOT_IN_PROGRESS,
	SOF_FW_BOOT_FAILED,
@@ -130,6 +132,9 @@ struct sof_dev_desc {
	unsigned int ipc_supported_mask;
	enum sof_ipc_type ipc_default;

	/* The platform supports DSPless mode */
	bool dspless_mode_supported;

	/* defaults paths for firmware, library and topology files */
	const char *default_fw_path[SOF_IPC_TYPE_COUNT];
	const char *default_lib_path[SOF_IPC_TYPE_COUNT];
+26 −5
Original line number Diff line number Diff line
@@ -208,6 +208,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
	/* set up platform component driver */
	snd_sof_new_platform_drv(sdev);

	if (sdev->dspless_mode_selected) {
		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
		goto skip_dsp_init;
	}

	/* register any debug/trace capabilities */
	ret = snd_sof_dbg_init(sdev);
	if (ret < 0) {
@@ -266,6 +271,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
		dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
	}

skip_dsp_init:
	/* hereafter all FW boot flows are for PM reasons */
	sdev->first_boot = false;

@@ -365,6 +371,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
	if (sof_core_debug)
		dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);

	if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) {
		if (plat_data->desc->dspless_mode_supported) {
			dev_info(dev, "Switching to DSPless mode\n");
			sdev->dspless_mode_selected = true;
		} else {
			dev_info(dev, "DSPless mode is not supported by the platform\n");
		}
	}

	/* check IPC support */
	if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
		dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
@@ -378,12 +393,18 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
		return ret;

	/* check all mandatory ops */
	if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
	    !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
	    !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
	    !sof_ops(sdev)->ipc_msg_data) {
	if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
		sof_ops_free(sdev);
		dev_err(dev, "missing mandatory ops\n");
		return -EINVAL;
	}

	if (!sdev->dspless_mode_selected &&
	    (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
	     !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
	     !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
		sof_ops_free(sdev);
		dev_err(dev, "error: missing mandatory ops\n");
		dev_err(dev, "missing mandatory DSP ops\n");
		return -EINVAL;
	}

+1 −0
Original line number Diff line number Diff line
@@ -370,6 +370,7 @@ static const struct soc_fw_state_info {
	const char *name;
} fw_state_dbg[] = {
	{SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
	{SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"},
	{SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"},
	{SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"},
	{SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"},
+32 −1
Original line number Diff line number Diff line
@@ -319,13 +319,44 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
	.post_trigger = hda_ipc3_post_trigger,
};

static struct hdac_ext_stream *
hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
			    struct snd_pcm_substream *substream)
{
	struct hdac_stream *hstream = substream->runtime->private_data;

	return stream_to_hdac_ext_stream(hstream);
}

static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
					  struct hdac_ext_stream *hext_stream,
					  unsigned int format_val)
{
	/*
	 * Save the format_val which was adjusted by the maxbps of the codec.
	 * This information is not available on the FE side since there we are
	 * using dummy_codec.
	 */
	hext_stream->hstream.format_val = format_val;
}

static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
	.get_hext_stream = hda_dspless_get_hext_stream,
	.setup_hext_stream = hda_dspless_setup_hext_stream,
};

#endif

const struct hda_dai_widget_dma_ops *
hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
	struct snd_sof_dai *sdai = swidget->private;
	struct snd_sof_dai *sdai;

	if (sdev->dspless_mode_selected)
		return &hda_dspless_dma_ops;

	sdai = swidget->private;

	switch (sdev->pdata->ipc_type) {
	case SOF_IPC:
+20 −5
Original line number Diff line number Diff line
@@ -31,11 +31,18 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
		   struct snd_sof_dai_config_data *data)
{
	struct snd_sof_widget *swidget = w->dobj.private;
	struct snd_soc_component *component = swidget->scomp;
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
	const struct sof_ipc_tplg_ops *tplg_ops;
	struct snd_soc_component *component;
	struct snd_sof_dev *sdev;
	int ret;

	if (!swidget)
		return 0;

	component = swidget->scomp;
	sdev = snd_soc_component_get_drvdata(component);
	tplg_ops = sof_ipc_get_ops(sdev, tplg);

	if (tplg_ops && tplg_ops->dai_config) {
		ret = tplg_ops->dai_config(sdev, swidget, flags, data);
		if (ret < 0) {
@@ -56,13 +63,21 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai
	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
	struct snd_sof_widget *swidget = w->dobj.private;
	struct snd_sof_dai *sdai = swidget->private;
	struct snd_sof_dai *sdai;

	/*
	 * The swidget parameter of hda_select_dai_widget_ops() is ignored in
	 * case of DSPless mode
	 */
	if (sdev->dspless_mode_selected)
		return hda_select_dai_widget_ops(sdev, NULL);

	sdai = swidget->private;

	/* select and set the DAI widget ops if not set already */
	if (!sdai->platform_private) {
		const struct hda_dai_widget_dma_ops *ops =
			hda_select_dai_widget_ops(sdev, swidget);

		if (!ops)
			return NULL;

Loading