162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2022 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <sound/pcm_params.h>
962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h>
1062306a36Sopenharmony_ci#include <sound/hda-mlink.h>
1162306a36Sopenharmony_ci#include <sound/sof/ipc4/header.h>
1262306a36Sopenharmony_ci#include <uapi/sound/sof/header.h>
1362306a36Sopenharmony_ci#include "../ipc4-priv.h"
1462306a36Sopenharmony_ci#include "../ipc4-topology.h"
1562306a36Sopenharmony_ci#include "../sof-priv.h"
1662306a36Sopenharmony_ci#include "../sof-audio.h"
1762306a36Sopenharmony_ci#include "hda.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* These ops are only applicable for the HDA DAI's in their current form */
2062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * This function checks if the host dma channel corresponding
2362306a36Sopenharmony_ci * to the link DMA stream_tag argument is assigned to one
2462306a36Sopenharmony_ci * of the FEs connected to the BE DAI.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
2762306a36Sopenharmony_ci			  int dir, int stream_tag)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct snd_pcm_substream *fe_substream;
3062306a36Sopenharmony_ci	struct hdac_stream *fe_hstream;
3162306a36Sopenharmony_ci	struct snd_soc_dpcm *dpcm;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	for_each_dpcm_fe(rtd, dir, dpcm) {
3462306a36Sopenharmony_ci		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
3562306a36Sopenharmony_ci		fe_hstream = fe_substream->runtime->private_data;
3662306a36Sopenharmony_ci		if (fe_hstream->stream_tag == stream_tag)
3762306a36Sopenharmony_ci			return true;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	return false;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic struct hdac_ext_stream *
4462306a36Sopenharmony_cihda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
4762306a36Sopenharmony_ci	struct sof_intel_hda_stream *hda_stream;
4862306a36Sopenharmony_ci	const struct sof_intel_dsp_desc *chip;
4962306a36Sopenharmony_ci	struct snd_sof_dev *sdev;
5062306a36Sopenharmony_ci	struct hdac_ext_stream *res = NULL;
5162306a36Sopenharmony_ci	struct hdac_stream *hstream = NULL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	int stream_dir = substream->stream;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!bus->ppcap) {
5662306a36Sopenharmony_ci		dev_err(bus->dev, "stream type not supported\n");
5762306a36Sopenharmony_ci		return NULL;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	spin_lock_irq(&bus->reg_lock);
6162306a36Sopenharmony_ci	list_for_each_entry(hstream, &bus->stream_list, list) {
6262306a36Sopenharmony_ci		struct hdac_ext_stream *hext_stream =
6362306a36Sopenharmony_ci			stream_to_hdac_ext_stream(hstream);
6462306a36Sopenharmony_ci		if (hstream->direction != substream->stream)
6562306a36Sopenharmony_ci			continue;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		hda_stream = hstream_to_sof_hda_stream(hext_stream);
6862306a36Sopenharmony_ci		sdev = hda_stream->sdev;
6962306a36Sopenharmony_ci		chip = get_chip_info(sdev->pdata);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		/* check if link is available */
7262306a36Sopenharmony_ci		if (!hext_stream->link_locked) {
7362306a36Sopenharmony_ci			/*
7462306a36Sopenharmony_ci			 * choose the first available link for platforms that do not have the
7562306a36Sopenharmony_ci			 * PROCEN_FMT_QUIRK set.
7662306a36Sopenharmony_ci			 */
7762306a36Sopenharmony_ci			if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
7862306a36Sopenharmony_ci				res = hext_stream;
7962306a36Sopenharmony_ci				break;
8062306a36Sopenharmony_ci			}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			if (hstream->opened) {
8362306a36Sopenharmony_ci				/*
8462306a36Sopenharmony_ci				 * check if the stream tag matches the stream
8562306a36Sopenharmony_ci				 * tag of one of the connected FEs
8662306a36Sopenharmony_ci				 */
8762306a36Sopenharmony_ci				if (hda_check_fes(rtd, stream_dir,
8862306a36Sopenharmony_ci						  hstream->stream_tag)) {
8962306a36Sopenharmony_ci					res = hext_stream;
9062306a36Sopenharmony_ci					break;
9162306a36Sopenharmony_ci				}
9262306a36Sopenharmony_ci			} else {
9362306a36Sopenharmony_ci				res = hext_stream;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci				/*
9662306a36Sopenharmony_ci				 * This must be a hostless stream.
9762306a36Sopenharmony_ci				 * So reserve the host DMA channel.
9862306a36Sopenharmony_ci				 */
9962306a36Sopenharmony_ci				hda_stream->host_reserved = 1;
10062306a36Sopenharmony_ci				break;
10162306a36Sopenharmony_ci			}
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (res) {
10662306a36Sopenharmony_ci		/* Make sure that host and link DMA is decoupled. */
10762306a36Sopenharmony_ci		snd_hdac_ext_stream_decouple_locked(bus, res, true);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		res->link_locked = 1;
11062306a36Sopenharmony_ci		res->link_substream = substream;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	spin_unlock_irq(&bus->reg_lock);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return res;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
11862306a36Sopenharmony_ci						   struct snd_soc_dai *cpu_dai,
11962306a36Sopenharmony_ci						   struct snd_pcm_substream *substream)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return snd_soc_dai_get_dma_data(cpu_dai, substream);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
12562306a36Sopenharmony_ci							struct snd_soc_dai *cpu_dai,
12662306a36Sopenharmony_ci							struct snd_pcm_substream *substream)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
12962306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
13062306a36Sopenharmony_ci	struct snd_sof_widget *swidget;
13162306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
13462306a36Sopenharmony_ci	swidget = w->dobj.private;
13562306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
13662306a36Sopenharmony_ci	pipeline = pipe_widget->private;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* mark pipeline so that it can be skipped during FE trigger */
13962306a36Sopenharmony_ci	pipeline->skip_during_fe_trigger = true;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return snd_soc_dai_get_dma_data(cpu_dai, substream);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
14562306a36Sopenharmony_ci						      struct snd_soc_dai *cpu_dai,
14662306a36Sopenharmony_ci						      struct snd_pcm_substream *substream)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
14962306a36Sopenharmony_ci	struct snd_soc_dai *dai;
15062306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* only allocate a stream_tag for the first DAI in the dailink */
15362306a36Sopenharmony_ci	dai = asoc_rtd_to_cpu(rtd, 0);
15462306a36Sopenharmony_ci	if (dai == cpu_dai)
15562306a36Sopenharmony_ci		hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
15662306a36Sopenharmony_ci	else
15762306a36Sopenharmony_ci		hext_stream = snd_soc_dai_get_dma_data(dai, substream);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!hext_stream)
16062306a36Sopenharmony_ci		return NULL;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return hext_stream;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
16862306a36Sopenharmony_ci				    struct snd_pcm_substream *substream)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
17162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
17262306a36Sopenharmony_ci	struct snd_soc_dai *dai;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* only release a stream_tag for the first DAI in the dailink */
17562306a36Sopenharmony_ci	dai = asoc_rtd_to_cpu(rtd, 0);
17662306a36Sopenharmony_ci	if (dai == cpu_dai)
17762306a36Sopenharmony_ci		snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
17862306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
18262306a36Sopenharmony_ci				  unsigned int format_val)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	snd_hdac_ext_stream_setup(hext_stream, format_val);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	snd_hdac_ext_stream_reset(hext_stream);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
19362306a36Sopenharmony_ci				     struct snd_pcm_substream *substream,
19462306a36Sopenharmony_ci				     struct hdac_stream *hstream)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
19762306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* set the hdac_stream in the codec dai */
20062306a36Sopenharmony_ci	snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
20462306a36Sopenharmony_ci					   struct snd_pcm_substream *substream,
20562306a36Sopenharmony_ci					   struct snd_pcm_hw_params *params)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
20862306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
20962306a36Sopenharmony_ci	unsigned int link_bps;
21062306a36Sopenharmony_ci	unsigned int format_val;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
21362306a36Sopenharmony_ci		link_bps = codec_dai->driver->playback.sig_bits;
21462306a36Sopenharmony_ci	else
21562306a36Sopenharmony_ci		link_bps = codec_dai->driver->capture.sig_bits;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
21862306a36Sopenharmony_ci						 params_format(params), link_bps, 0);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
22162306a36Sopenharmony_ci		params_rate(params), params_channels(params), params_format(params));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return format_val;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
22762306a36Sopenharmony_ci					   struct snd_pcm_substream *substream)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
23062306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
23162306a36Sopenharmony_ci	struct hdac_bus *bus = sof_to_bus(sdev);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
23762306a36Sopenharmony_ci					       struct snd_pcm_substream *substream,
23862306a36Sopenharmony_ci					       struct snd_pcm_hw_params *params)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	unsigned int format_val;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
24362306a36Sopenharmony_ci						 params_format(params),
24462306a36Sopenharmony_ci						 params_physical_width(params),
24562306a36Sopenharmony_ci						 0);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
24862306a36Sopenharmony_ci		params_rate(params), params_channels(params), params_format(params));
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return format_val;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
25462306a36Sopenharmony_ci					    struct snd_pcm_substream *substream,
25562306a36Sopenharmony_ci					    struct snd_pcm_hw_params *params)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	unsigned int format_val;
25862306a36Sopenharmony_ci	snd_pcm_format_t format;
25962306a36Sopenharmony_ci	unsigned int channels;
26062306a36Sopenharmony_ci	unsigned int width;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	channels = params_channels(params);
26362306a36Sopenharmony_ci	format = params_format(params);
26462306a36Sopenharmony_ci	width = params_physical_width(params);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (format == SNDRV_PCM_FORMAT_S16_LE) {
26762306a36Sopenharmony_ci		format = SNDRV_PCM_FORMAT_S32_LE;
26862306a36Sopenharmony_ci		channels /= 2;
26962306a36Sopenharmony_ci		width = 32;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	format_val = snd_hdac_calc_stream_format(params_rate(params), channels,
27362306a36Sopenharmony_ci						 format,
27462306a36Sopenharmony_ci						 width,
27562306a36Sopenharmony_ci						 0);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
27862306a36Sopenharmony_ci		params_rate(params), channels, format);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return format_val;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev,
28462306a36Sopenharmony_ci					   struct snd_pcm_substream *substream)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct hdac_bus *bus = sof_to_bus(sdev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return hdac_bus_eml_ssp_get_hlink(bus);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev,
29262306a36Sopenharmony_ci					    struct snd_pcm_substream *substream)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct hdac_bus *bus = sof_to_bus(sdev);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return hdac_bus_eml_dmic_get_hlink(bus);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,
30062306a36Sopenharmony_ci					   struct snd_pcm_substream *substream)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct hdac_bus *bus = sof_to_bus(sdev);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return hdac_bus_eml_sdw_get_hlink(bus);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
30862306a36Sopenharmony_ci				struct snd_pcm_substream *substream, int cmd)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
31162306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
31262306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
31362306a36Sopenharmony_ci	struct snd_sof_widget *swidget;
31462306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w;
31562306a36Sopenharmony_ci	int ret = 0;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
31862306a36Sopenharmony_ci	swidget = w->dobj.private;
31962306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
32062306a36Sopenharmony_ci	pipeline = pipe_widget->private;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (pipe_widget->instance_id < 0)
32362306a36Sopenharmony_ci		return 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mutex_lock(&ipc4_data->pipeline_state_mutex);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	switch (cmd) {
32862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
32962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
33262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
33362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
33462306a36Sopenharmony_ci		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
33562306a36Sopenharmony_ci						  SOF_IPC4_PIPE_PAUSED);
33662306a36Sopenharmony_ci		if (ret < 0)
33762306a36Sopenharmony_ci			goto out;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		pipeline->state = SOF_IPC4_PIPE_PAUSED;
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
34362306a36Sopenharmony_ci		ret = -EINVAL;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ciout:
34662306a36Sopenharmony_ci	mutex_unlock(&ipc4_data->pipeline_state_mutex);
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
35162306a36Sopenharmony_ci		       struct snd_pcm_substream *substream, int cmd)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	switch (cmd) {
35662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
35762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
35862306a36Sopenharmony_ci		snd_hdac_ext_stream_start(hext_stream);
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
36162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
36262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
36362306a36Sopenharmony_ci		snd_hdac_ext_stream_clear(hext_stream);
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	default:
36662306a36Sopenharmony_ci		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
36762306a36Sopenharmony_ci		return -EINVAL;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
37462306a36Sopenharmony_ci				 struct snd_pcm_substream *substream, int cmd)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
37762306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
37862306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
37962306a36Sopenharmony_ci	struct snd_sof_widget *swidget;
38062306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w;
38162306a36Sopenharmony_ci	int ret = 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
38462306a36Sopenharmony_ci	swidget = w->dobj.private;
38562306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
38662306a36Sopenharmony_ci	pipeline = pipe_widget->private;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (pipe_widget->instance_id < 0)
38962306a36Sopenharmony_ci		return 0;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mutex_lock(&ipc4_data->pipeline_state_mutex);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	switch (cmd) {
39462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
39562306a36Sopenharmony_ci		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
39662306a36Sopenharmony_ci			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
39762306a36Sopenharmony_ci							  SOF_IPC4_PIPE_PAUSED);
39862306a36Sopenharmony_ci			if (ret < 0)
39962306a36Sopenharmony_ci				goto out;
40062306a36Sopenharmony_ci			pipeline->state = SOF_IPC4_PIPE_PAUSED;
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
40462306a36Sopenharmony_ci						  SOF_IPC4_PIPE_RUNNING);
40562306a36Sopenharmony_ci		if (ret < 0)
40662306a36Sopenharmony_ci			goto out;
40762306a36Sopenharmony_ci		pipeline->state = SOF_IPC4_PIPE_RUNNING;
40862306a36Sopenharmony_ci		swidget->spipe->started_count++;
40962306a36Sopenharmony_ci		break;
41062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
41162306a36Sopenharmony_ci		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
41262306a36Sopenharmony_ci						  SOF_IPC4_PIPE_RUNNING);
41362306a36Sopenharmony_ci		if (ret < 0)
41462306a36Sopenharmony_ci			goto out;
41562306a36Sopenharmony_ci		pipeline->state = SOF_IPC4_PIPE_RUNNING;
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
41862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
41962306a36Sopenharmony_ci		/*
42062306a36Sopenharmony_ci		 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
42162306a36Sopenharmony_ci		 * been stopped. So, clear the started_count so that the pipeline can be reset
42262306a36Sopenharmony_ci		 */
42362306a36Sopenharmony_ci		swidget->spipe->started_count = 0;
42462306a36Sopenharmony_ci		break;
42562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	default:
42862306a36Sopenharmony_ci		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
42962306a36Sopenharmony_ci		ret = -EINVAL;
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ciout:
43362306a36Sopenharmony_ci	mutex_unlock(&ipc4_data->pipeline_state_mutex);
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
43862306a36Sopenharmony_ci							    struct snd_soc_dai *cpu_dai,
43962306a36Sopenharmony_ci							    struct snd_pcm_substream *substream)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
44262306a36Sopenharmony_ci	struct snd_sof_widget *swidget = w->dobj.private;
44362306a36Sopenharmony_ci	struct snd_sof_dai *dai = swidget->private;
44462306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier = dai->private;
44562306a36Sopenharmony_ci	struct sof_ipc4_alh_configuration_blob *blob;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/*
45062306a36Sopenharmony_ci	 * Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using
45162306a36Sopenharmony_ci	 * the multi-gateway firmware configuration. The DMA hardware can take care of
45262306a36Sopenharmony_ci	 * multiple links without needing any firmware assistance
45362306a36Sopenharmony_ci	 */
45462306a36Sopenharmony_ci	blob->alh_cfg.device_count = 1;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
46062306a36Sopenharmony_ci	.get_hext_stream = hda_ipc4_get_hext_stream,
46162306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
46262306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
46362306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
46462306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
46562306a36Sopenharmony_ci	.pre_trigger = hda_ipc4_pre_trigger,
46662306a36Sopenharmony_ci	.trigger = hda_trigger,
46762306a36Sopenharmony_ci	.post_trigger = hda_ipc4_post_trigger,
46862306a36Sopenharmony_ci	.codec_dai_set_stream = hda_codec_dai_set_stream,
46962306a36Sopenharmony_ci	.calc_stream_format = hda_calc_stream_format,
47062306a36Sopenharmony_ci	.get_hlink = hda_get_hlink,
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = {
47462306a36Sopenharmony_ci	.get_hext_stream = hda_ipc4_get_hext_stream,
47562306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
47662306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
47762306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
47862306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
47962306a36Sopenharmony_ci	.pre_trigger = hda_ipc4_pre_trigger,
48062306a36Sopenharmony_ci	.trigger = hda_trigger,
48162306a36Sopenharmony_ci	.post_trigger = hda_ipc4_post_trigger,
48262306a36Sopenharmony_ci	.calc_stream_format = generic_calc_stream_format,
48362306a36Sopenharmony_ci	.get_hlink = ssp_get_hlink,
48462306a36Sopenharmony_ci};
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
48762306a36Sopenharmony_ci	.get_hext_stream = hda_ipc4_get_hext_stream,
48862306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
48962306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
49062306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
49162306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
49262306a36Sopenharmony_ci	.pre_trigger = hda_ipc4_pre_trigger,
49362306a36Sopenharmony_ci	.trigger = hda_trigger,
49462306a36Sopenharmony_ci	.post_trigger = hda_ipc4_post_trigger,
49562306a36Sopenharmony_ci	.calc_stream_format = dmic_calc_stream_format,
49662306a36Sopenharmony_ci	.get_hlink = dmic_get_hlink,
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
50062306a36Sopenharmony_ci	.get_hext_stream = sdw_hda_ipc4_get_hext_stream,
50162306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
50262306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
50362306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
50462306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
50562306a36Sopenharmony_ci	.pre_trigger = hda_ipc4_pre_trigger,
50662306a36Sopenharmony_ci	.trigger = hda_trigger,
50762306a36Sopenharmony_ci	.post_trigger = hda_ipc4_post_trigger,
50862306a36Sopenharmony_ci	.calc_stream_format = generic_calc_stream_format,
50962306a36Sopenharmony_ci	.get_hlink = sdw_get_hlink,
51062306a36Sopenharmony_ci};
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
51362306a36Sopenharmony_ci	.get_hext_stream = hda_get_hext_stream,
51462306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
51562306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
51662306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
51762306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
51862306a36Sopenharmony_ci	.trigger = hda_trigger,
51962306a36Sopenharmony_ci	.codec_dai_set_stream = hda_codec_dai_set_stream,
52062306a36Sopenharmony_ci	.calc_stream_format = hda_calc_stream_format,
52162306a36Sopenharmony_ci	.get_hlink = hda_get_hlink,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
52562306a36Sopenharmony_ci				 struct snd_pcm_substream *substream, int cmd)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
52862306a36Sopenharmony_ci	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	switch (cmd) {
53162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
53262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
53362306a36Sopenharmony_ci	{
53462306a36Sopenharmony_ci		struct snd_sof_dai_config_data data = { 0 };
53562306a36Sopenharmony_ci		int ret;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		data.dai_data = DMA_CHAN_INVALID;
53862306a36Sopenharmony_ci		ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
53962306a36Sopenharmony_ci		if (ret < 0)
54062306a36Sopenharmony_ci			return ret;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_STOP)
54362306a36Sopenharmony_ci			return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
54862306a36Sopenharmony_ci		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
54962306a36Sopenharmony_ci	default:
55062306a36Sopenharmony_ci		break;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return 0;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
55762306a36Sopenharmony_ci	.get_hext_stream = hda_get_hext_stream,
55862306a36Sopenharmony_ci	.assign_hext_stream = hda_assign_hext_stream,
55962306a36Sopenharmony_ci	.release_hext_stream = hda_release_hext_stream,
56062306a36Sopenharmony_ci	.setup_hext_stream = hda_setup_hext_stream,
56162306a36Sopenharmony_ci	.reset_hext_stream = hda_reset_hext_stream,
56262306a36Sopenharmony_ci	.trigger = hda_trigger,
56362306a36Sopenharmony_ci	.post_trigger = hda_ipc3_post_trigger,
56462306a36Sopenharmony_ci	.codec_dai_set_stream = hda_codec_dai_set_stream,
56562306a36Sopenharmony_ci	.calc_stream_format = hda_calc_stream_format,
56662306a36Sopenharmony_ci	.get_hlink = hda_get_hlink,
56762306a36Sopenharmony_ci};
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic struct hdac_ext_stream *
57062306a36Sopenharmony_cihda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
57162306a36Sopenharmony_ci			    struct snd_pcm_substream *substream)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct hdac_stream *hstream = substream->runtime->private_data;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return stream_to_hdac_ext_stream(hstream);
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
57962306a36Sopenharmony_ci					  struct hdac_ext_stream *hext_stream,
58062306a36Sopenharmony_ci					  unsigned int format_val)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	/*
58362306a36Sopenharmony_ci	 * Save the format_val which was adjusted by the maxbps of the codec.
58462306a36Sopenharmony_ci	 * This information is not available on the FE side since there we are
58562306a36Sopenharmony_ci	 * using dummy_codec.
58662306a36Sopenharmony_ci	 */
58762306a36Sopenharmony_ci	hext_stream->hstream.format_val = format_val;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
59162306a36Sopenharmony_ci	.get_hext_stream = hda_dspless_get_hext_stream,
59262306a36Sopenharmony_ci	.setup_hext_stream = hda_dspless_setup_hext_stream,
59362306a36Sopenharmony_ci	.codec_dai_set_stream = hda_codec_dai_set_stream,
59462306a36Sopenharmony_ci	.calc_stream_format = hda_calc_stream_format,
59562306a36Sopenharmony_ci	.get_hlink = hda_get_hlink,
59662306a36Sopenharmony_ci};
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci#endif
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ciconst struct hda_dai_widget_dma_ops *
60162306a36Sopenharmony_cihda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
60462306a36Sopenharmony_ci	struct snd_sof_dai *sdai;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (sdev->dspless_mode_selected)
60762306a36Sopenharmony_ci		return &hda_dspless_dma_ops;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	sdai = swidget->private;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	switch (sdev->pdata->ipc_type) {
61262306a36Sopenharmony_ci	case SOF_IPC:
61362306a36Sopenharmony_ci	{
61462306a36Sopenharmony_ci		struct sof_dai_private_data *private = sdai->private;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if (private->dai_config->type == SOF_DAI_INTEL_HDA)
61762306a36Sopenharmony_ci			return &hda_ipc3_dma_ops;
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	case SOF_INTEL_IPC4:
62162306a36Sopenharmony_ci	{
62262306a36Sopenharmony_ci		struct sof_ipc4_copier *ipc4_copier = sdai->private;
62362306a36Sopenharmony_ci		const struct sof_intel_dsp_desc *chip;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		chip = get_chip_info(sdev->pdata);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		switch (ipc4_copier->dai_type) {
62862306a36Sopenharmony_ci		case SOF_DAI_INTEL_HDA:
62962306a36Sopenharmony_ci		{
63062306a36Sopenharmony_ci			struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
63162306a36Sopenharmony_ci			struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			if (pipeline->use_chain_dma)
63462306a36Sopenharmony_ci				return &hda_ipc4_chain_dma_ops;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci			return &hda_ipc4_dma_ops;
63762306a36Sopenharmony_ci		}
63862306a36Sopenharmony_ci		case SOF_DAI_INTEL_SSP:
63962306a36Sopenharmony_ci			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
64062306a36Sopenharmony_ci				return NULL;
64162306a36Sopenharmony_ci			return &ssp_ipc4_dma_ops;
64262306a36Sopenharmony_ci		case SOF_DAI_INTEL_DMIC:
64362306a36Sopenharmony_ci			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
64462306a36Sopenharmony_ci				return NULL;
64562306a36Sopenharmony_ci			return &dmic_ipc4_dma_ops;
64662306a36Sopenharmony_ci		case SOF_DAI_INTEL_ALH:
64762306a36Sopenharmony_ci			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
64862306a36Sopenharmony_ci				return NULL;
64962306a36Sopenharmony_ci			return &sdw_ipc4_dma_ops;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		default:
65262306a36Sopenharmony_ci			break;
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	default:
65762306a36Sopenharmony_ci		break;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci#endif
66062306a36Sopenharmony_ci	return NULL;
66162306a36Sopenharmony_ci}
662