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