cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hda-dai.c (19539B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// This file is provided under a dual BSD/GPLv2 license.  When using or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2018 Intel Corporation. All rights reserved.
      7//
      8// Authors: Keyon Jie <yang.jie@linux.intel.com>
      9//
     10
     11#include <sound/pcm_params.h>
     12#include <sound/hdaudio_ext.h>
     13#include "../sof-priv.h"
     14#include "../sof-audio.h"
     15#include "hda.h"
     16
     17#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
     18
     19struct hda_pipe_params {
     20	u32 ch;
     21	u32 s_freq;
     22	u32 s_fmt;
     23	u8 linktype;
     24	snd_pcm_format_t format;
     25	int link_index;
     26	int stream;
     27	unsigned int link_bps;
     28};
     29
     30/*
     31 * This function checks if the host dma channel corresponding
     32 * to the link DMA stream_tag argument is assigned to one
     33 * of the FEs connected to the BE DAI.
     34 */
     35static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
     36			  int dir, int stream_tag)
     37{
     38	struct snd_pcm_substream *fe_substream;
     39	struct hdac_stream *fe_hstream;
     40	struct snd_soc_dpcm *dpcm;
     41
     42	for_each_dpcm_fe(rtd, dir, dpcm) {
     43		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
     44		fe_hstream = fe_substream->runtime->private_data;
     45		if (fe_hstream->stream_tag == stream_tag)
     46			return true;
     47	}
     48
     49	return false;
     50}
     51
     52static struct hdac_ext_stream *
     53hda_link_stream_assign(struct hdac_bus *bus,
     54		       struct snd_pcm_substream *substream)
     55{
     56	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     57	struct sof_intel_hda_stream *hda_stream;
     58	const struct sof_intel_dsp_desc *chip;
     59	struct snd_sof_dev *sdev;
     60	struct hdac_ext_stream *res = NULL;
     61	struct hdac_stream *hstream = NULL;
     62
     63	int stream_dir = substream->stream;
     64
     65	if (!bus->ppcap) {
     66		dev_err(bus->dev, "stream type not supported\n");
     67		return NULL;
     68	}
     69
     70	spin_lock_irq(&bus->reg_lock);
     71	list_for_each_entry(hstream, &bus->stream_list, list) {
     72		struct hdac_ext_stream *hext_stream =
     73			stream_to_hdac_ext_stream(hstream);
     74		if (hstream->direction != substream->stream)
     75			continue;
     76
     77		hda_stream = hstream_to_sof_hda_stream(hext_stream);
     78		sdev = hda_stream->sdev;
     79		chip = get_chip_info(sdev->pdata);
     80
     81		/* check if link is available */
     82		if (!hext_stream->link_locked) {
     83			/*
     84			 * choose the first available link for platforms that do not have the
     85			 * PROCEN_FMT_QUIRK set.
     86			 */
     87			if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
     88				res = hext_stream;
     89				break;
     90			}
     91
     92			if (hstream->opened) {
     93				/*
     94				 * check if the stream tag matches the stream
     95				 * tag of one of the connected FEs
     96				 */
     97				if (hda_check_fes(rtd, stream_dir,
     98						  hstream->stream_tag)) {
     99					res = hext_stream;
    100					break;
    101				}
    102			} else {
    103				res = hext_stream;
    104
    105				/*
    106				 * This must be a hostless stream.
    107				 * So reserve the host DMA channel.
    108				 */
    109				hda_stream->host_reserved = 1;
    110				break;
    111			}
    112		}
    113	}
    114
    115	if (res) {
    116		/*
    117		 * Decouple host and link DMA. The decoupled flag
    118		 * is updated in snd_hdac_ext_stream_decouple().
    119		 */
    120		if (!res->decoupled)
    121			snd_hdac_ext_stream_decouple_locked(bus, res, true);
    122
    123		res->link_locked = 1;
    124		res->link_substream = substream;
    125	}
    126	spin_unlock_irq(&bus->reg_lock);
    127
    128	return res;
    129}
    130
    131static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
    132				struct hdac_stream *hstream,
    133				struct snd_soc_dai *cpu_dai,
    134				struct snd_soc_dai *codec_dai,
    135				bool trigger_suspend_stop)
    136{
    137	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
    138	struct hdac_bus *bus = hstream->bus;
    139	struct sof_intel_hda_stream *hda_stream;
    140	struct hdac_ext_link *link;
    141	int stream_tag;
    142
    143	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
    144	if (!link)
    145		return -EINVAL;
    146
    147	if (trigger_suspend_stop)
    148		snd_hdac_ext_link_stream_clear(hext_stream);
    149
    150	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    151		stream_tag = hdac_stream(hext_stream)->stream_tag;
    152		snd_hdac_ext_link_clear_stream_id(link, stream_tag);
    153	}
    154	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
    155	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
    156	hext_stream->link_prepared = 0;
    157
    158	/* free the host DMA channel reserved by hostless streams */
    159	hda_stream = hstream_to_sof_hda_stream(hext_stream);
    160	hda_stream->host_reserved = 0;
    161
    162	return 0;
    163}
    164
    165static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
    166			       struct hda_pipe_params *params)
    167{
    168	struct hdac_stream *hstream = &hext_stream->hstream;
    169	unsigned char stream_tag = hstream->stream_tag;
    170	struct hdac_bus *bus = hstream->bus;
    171	struct hdac_ext_link *link;
    172	unsigned int format_val;
    173
    174	snd_hdac_ext_stream_decouple(bus, hext_stream, true);
    175	snd_hdac_ext_link_stream_reset(hext_stream);
    176
    177	format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
    178						 params->format,
    179						 params->link_bps, 0);
    180
    181	dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
    182		format_val, params->s_freq, params->ch, params->format);
    183
    184	snd_hdac_ext_link_stream_setup(hext_stream, format_val);
    185
    186	if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
    187		list_for_each_entry(link, &bus->hlink_list, list) {
    188			if (link->index == params->link_index)
    189				snd_hdac_ext_link_set_stream_id(link,
    190								stream_tag);
    191		}
    192	}
    193
    194	hext_stream->link_prepared = 1;
    195
    196	return 0;
    197}
    198
    199static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
    200				  struct snd_pcm_hw_params *params)
    201{
    202	struct hdac_stream *hstream = substream->runtime->private_data;
    203	struct hdac_ext_stream *hext_stream;
    204	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    205	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    206	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
    207	struct hda_pipe_params p_params = {0};
    208	struct hdac_bus *bus = hstream->bus;
    209	struct hdac_ext_link *link;
    210
    211	/* get stored dma data if resuming from system suspend */
    212	hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
    213	if (!hext_stream) {
    214		hext_stream = hda_link_stream_assign(bus, substream);
    215		if (!hext_stream)
    216			return -EBUSY;
    217
    218		snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
    219	}
    220
    221	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
    222	if (!link)
    223		return -EINVAL;
    224
    225	/* set the hdac_stream in the codec dai */
    226	snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
    227
    228	p_params.s_fmt = snd_pcm_format_width(params_format(params));
    229	p_params.ch = params_channels(params);
    230	p_params.s_freq = params_rate(params);
    231	p_params.stream = substream->stream;
    232	p_params.link_index = link->index;
    233	p_params.format = params_format(params);
    234
    235	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    236		p_params.link_bps = codec_dai->driver->playback.sig_bits;
    237	else
    238		p_params.link_bps = codec_dai->driver->capture.sig_bits;
    239
    240	return hda_link_dma_params(hext_stream, &p_params);
    241}
    242
    243static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
    244{
    245	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    246	int stream = substream->stream;
    247
    248	return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
    249}
    250
    251static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
    252{
    253	struct hdac_stream *hstream = substream->runtime->private_data;
    254	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    255	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    256	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
    257	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
    258	int ret;
    259
    260	dev_dbg(cpu_dai->dev, "%s: cmd=%d\n", __func__, cmd);
    261	if (!hext_stream)
    262		return 0;
    263
    264	switch (cmd) {
    265	case SNDRV_PCM_TRIGGER_START:
    266	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    267		snd_hdac_ext_link_stream_start(hext_stream);
    268		break;
    269	case SNDRV_PCM_TRIGGER_SUSPEND:
    270	case SNDRV_PCM_TRIGGER_STOP:
    271		ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, true);
    272		if (ret < 0)
    273			return ret;
    274
    275		break;
    276	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    277		snd_hdac_ext_link_stream_clear(hext_stream);
    278
    279		break;
    280	default:
    281		return -EINVAL;
    282	}
    283	return 0;
    284}
    285
    286static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
    287{
    288	struct hdac_stream *hstream = substream->runtime->private_data;
    289	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    290	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    291	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
    292	struct hdac_ext_stream *hext_stream;
    293
    294	hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
    295	if (!hext_stream)
    296		return 0;
    297
    298	return hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, false);
    299}
    300
    301static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
    302				 int channel, bool widget_setup)
    303{
    304	struct snd_sof_dai_config_data data;
    305
    306	data.dai_data = channel;
    307
    308	/* set up/free DAI widget and send DAI_CONFIG IPC */
    309	if (widget_setup)
    310		return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
    311
    312	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
    313}
    314
    315static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
    316				    struct snd_pcm_hw_params *params,
    317				    struct snd_soc_dai *dai)
    318{
    319	struct hdac_ext_stream *hext_stream;
    320	struct snd_soc_dapm_widget *w;
    321	int stream_tag;
    322
    323	hext_stream = snd_soc_dai_get_dma_data(dai, substream);
    324	if (!hext_stream)
    325		return -EINVAL;
    326
    327	stream_tag = hdac_stream(hext_stream)->stream_tag;
    328
    329	w = snd_soc_dai_get_widget(dai, substream->stream);
    330
    331	/* set up the DAI widget and send the DAI_CONFIG with the new tag */
    332	return hda_dai_widget_update(w, stream_tag - 1, true);
    333}
    334
    335static int hda_dai_hw_params(struct snd_pcm_substream *substream,
    336			     struct snd_pcm_hw_params *params,
    337			     struct snd_soc_dai *dai)
    338{
    339	struct hdac_ext_stream *hext_stream =
    340				snd_soc_dai_get_dma_data(dai, substream);
    341	int ret;
    342
    343	if (hext_stream && hext_stream->link_prepared)
    344		return 0;
    345
    346	ret = hda_link_dma_hw_params(substream, params);
    347	if (ret < 0)
    348		return ret;
    349
    350	return hda_dai_hw_params_update(substream, params, dai);
    351}
    352
    353
    354static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
    355{
    356	struct snd_sof_widget *swidget = w->dobj.private;
    357	struct snd_soc_component *component = swidget->scomp;
    358	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    359	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
    360	int ret = 0;
    361
    362	if (tplg_ops->dai_config) {
    363		ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
    364		if (ret < 0)
    365			dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
    366				w->name);
    367	}
    368
    369	return ret;
    370}
    371
    372static int ipc3_hda_dai_prepare(struct snd_pcm_substream *substream,
    373				struct snd_soc_dai *dai)
    374{
    375	struct hdac_ext_stream *hext_stream =
    376				snd_soc_dai_get_dma_data(dai, substream);
    377	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
    378	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    379	int stream = substream->stream;
    380	int ret;
    381
    382	if (hext_stream && hext_stream->link_prepared)
    383		return 0;
    384
    385	dev_dbg(sdev->dev, "%s: prepare stream dir %d\n", __func__, substream->stream);
    386
    387	ret = hda_link_dma_prepare(substream);
    388	if (ret < 0)
    389		return ret;
    390
    391	return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
    392}
    393
    394static int hda_dai_hw_free_ipc(int stream, /* direction */
    395			       struct snd_soc_dai *dai)
    396{
    397	struct snd_soc_dapm_widget *w;
    398
    399	w = snd_soc_dai_get_widget(dai, stream);
    400
    401	/* free the link DMA channel in the FW and the DAI widget */
    402	return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
    403}
    404
    405static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
    406				int cmd, struct snd_soc_dai *dai)
    407{
    408	struct snd_soc_dapm_widget *w;
    409	int ret;
    410
    411	ret = hda_link_dma_trigger(substream, cmd);
    412	if (ret < 0)
    413		return ret;
    414
    415	w = snd_soc_dai_get_widget(dai, substream->stream);
    416
    417	dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd);
    418	switch (cmd) {
    419	case SNDRV_PCM_TRIGGER_SUSPEND:
    420	case SNDRV_PCM_TRIGGER_STOP:
    421		/*
    422		 * free DAI widget during stop/suspend to keep widget use_count's balanced.
    423		 */
    424		ret = hda_dai_hw_free_ipc(substream->stream, dai);
    425		if (ret < 0)
    426			return ret;
    427
    428		break;
    429	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    430		ret = hda_dai_config_pause_push_ipc(w);
    431		if (ret < 0)
    432			return ret;
    433		break;
    434
    435	default:
    436		break;
    437	}
    438	return 0;
    439}
    440
    441static int hda_dai_hw_free(struct snd_pcm_substream *substream,
    442			   struct snd_soc_dai *dai)
    443{
    444	int ret;
    445
    446	ret = hda_link_dma_hw_free(substream);
    447	if (ret < 0)
    448		return ret;
    449
    450	return hda_dai_hw_free_ipc(substream->stream, dai);
    451}
    452
    453static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
    454	.hw_params = hda_dai_hw_params,
    455	.hw_free = hda_dai_hw_free,
    456	.trigger = ipc3_hda_dai_trigger,
    457	.prepare = ipc3_hda_dai_prepare,
    458};
    459
    460static int hda_dai_suspend(struct hdac_bus *bus)
    461{
    462	struct snd_soc_pcm_runtime *rtd;
    463	struct hdac_ext_stream *hext_stream;
    464	struct hdac_stream *s;
    465	int ret;
    466
    467	/* set internal flag for BE */
    468	list_for_each_entry(s, &bus->stream_list, list) {
    469
    470		hext_stream = stream_to_hdac_ext_stream(s);
    471
    472		/*
    473		 * clear stream. This should already be taken care for running
    474		 * streams when the SUSPEND trigger is called. But paused
    475		 * streams do not get suspended, so this needs to be done
    476		 * explicitly during suspend.
    477		 */
    478		if (hext_stream->link_substream) {
    479			struct snd_soc_dai *cpu_dai;
    480			struct snd_soc_dai *codec_dai;
    481
    482			rtd = asoc_substream_to_rtd(hext_stream->link_substream);
    483			cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    484			codec_dai = asoc_rtd_to_codec(rtd, 0);
    485
    486			ret = hda_link_dma_cleanup(hext_stream->link_substream, s,
    487						   cpu_dai, codec_dai, false);
    488			if (ret < 0)
    489				return ret;
    490
    491			/* for consistency with TRIGGER_SUSPEND we free DAI resources */
    492			ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
    493			if (ret < 0)
    494				return ret;
    495		}
    496	}
    497
    498	return 0;
    499}
    500#endif
    501
    502/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
    503struct ssp_dai_dma_data {
    504	bool setup;
    505};
    506
    507static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
    508				 bool setup)
    509{
    510	struct snd_soc_dapm_widget *w;
    511
    512	w = snd_soc_dai_get_widget(dai, substream->stream);
    513
    514	if (setup)
    515		return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
    516
    517	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
    518}
    519
    520static int ssp_dai_startup(struct snd_pcm_substream *substream,
    521			   struct snd_soc_dai *dai)
    522{
    523	struct ssp_dai_dma_data *dma_data;
    524
    525	dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
    526	if (!dma_data)
    527		return -ENOMEM;
    528
    529	snd_soc_dai_set_dma_data(dai, substream, dma_data);
    530
    531	return 0;
    532}
    533
    534static int ssp_dai_setup(struct snd_pcm_substream *substream,
    535			 struct snd_soc_dai *dai,
    536			 bool setup)
    537{
    538	struct ssp_dai_dma_data *dma_data;
    539	int ret = 0;
    540
    541	dma_data = snd_soc_dai_get_dma_data(dai, substream);
    542	if (!dma_data) {
    543		dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
    544		return -EIO;
    545	}
    546
    547	if (dma_data->setup != setup) {
    548		ret = ssp_dai_setup_or_free(substream, dai, setup);
    549		if (!ret)
    550			dma_data->setup = setup;
    551	}
    552	return ret;
    553}
    554
    555static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
    556			     struct snd_pcm_hw_params *params,
    557			     struct snd_soc_dai *dai)
    558{
    559	/* params are ignored for now */
    560	return ssp_dai_setup(substream, dai, true);
    561}
    562
    563static int ssp_dai_prepare(struct snd_pcm_substream *substream,
    564			   struct snd_soc_dai *dai)
    565{
    566	/*
    567	 * the SSP will only be reconfigured during resume operations and
    568	 * not in case of xruns
    569	 */
    570	return ssp_dai_setup(substream, dai, true);
    571}
    572
    573static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
    574				int cmd, struct snd_soc_dai *dai)
    575{
    576	if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
    577		return 0;
    578
    579	return ssp_dai_setup(substream, dai, false);
    580}
    581
    582static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
    583			   struct snd_soc_dai *dai)
    584{
    585	return ssp_dai_setup(substream, dai, false);
    586}
    587
    588static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
    589			     struct snd_soc_dai *dai)
    590{
    591	struct ssp_dai_dma_data *dma_data;
    592
    593	dma_data = snd_soc_dai_get_dma_data(dai, substream);
    594	if (!dma_data) {
    595		dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
    596		return;
    597	}
    598	snd_soc_dai_set_dma_data(dai, substream, NULL);
    599	kfree(dma_data);
    600}
    601
    602static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
    603	.startup = ssp_dai_startup,
    604	.hw_params = ssp_dai_hw_params,
    605	.prepare = ssp_dai_prepare,
    606	.trigger = ipc3_ssp_dai_trigger,
    607	.hw_free = ssp_dai_hw_free,
    608	.shutdown = ssp_dai_shutdown,
    609};
    610
    611void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
    612{
    613	int i;
    614
    615	switch (sdev->pdata->ipc_type) {
    616	case SOF_IPC:
    617		for (i = 0; i < ops->num_drv; i++) {
    618			if (strstr(ops->drv[i].name, "SSP")) {
    619				ops->drv[i].ops = &ipc3_ssp_dai_ops;
    620				continue;
    621			}
    622#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
    623			if (strstr(ops->drv[i].name, "iDisp") ||
    624			    strstr(ops->drv[i].name, "Analog") ||
    625			    strstr(ops->drv[i].name, "Digital"))
    626				ops->drv[i].ops = &ipc3_hda_dai_ops;
    627#endif
    628		}
    629		break;
    630	default:
    631		break;
    632	}
    633}
    634
    635/*
    636 * common dai driver for skl+ platforms.
    637 * some products who use this DAI array only physically have a subset of
    638 * the DAIs, but no harm is done here by adding the whole set.
    639 */
    640struct snd_soc_dai_driver skl_dai[] = {
    641{
    642	.name = "SSP0 Pin",
    643	.playback = {
    644		.channels_min = 1,
    645		.channels_max = 8,
    646	},
    647	.capture = {
    648		.channels_min = 1,
    649		.channels_max = 8,
    650	},
    651},
    652{
    653	.name = "SSP1 Pin",
    654	.playback = {
    655		.channels_min = 1,
    656		.channels_max = 8,
    657	},
    658	.capture = {
    659		.channels_min = 1,
    660		.channels_max = 8,
    661	},
    662},
    663{
    664	.name = "SSP2 Pin",
    665	.playback = {
    666		.channels_min = 1,
    667		.channels_max = 8,
    668	},
    669	.capture = {
    670		.channels_min = 1,
    671		.channels_max = 8,
    672	},
    673},
    674{
    675	.name = "SSP3 Pin",
    676	.playback = {
    677		.channels_min = 1,
    678		.channels_max = 8,
    679	},
    680	.capture = {
    681		.channels_min = 1,
    682		.channels_max = 8,
    683	},
    684},
    685{
    686	.name = "SSP4 Pin",
    687	.playback = {
    688		.channels_min = 1,
    689		.channels_max = 8,
    690	},
    691	.capture = {
    692		.channels_min = 1,
    693		.channels_max = 8,
    694	},
    695},
    696{
    697	.name = "SSP5 Pin",
    698	.playback = {
    699		.channels_min = 1,
    700		.channels_max = 8,
    701	},
    702	.capture = {
    703		.channels_min = 1,
    704		.channels_max = 8,
    705	},
    706},
    707{
    708	.name = "DMIC01 Pin",
    709	.capture = {
    710		.channels_min = 1,
    711		.channels_max = 4,
    712	},
    713},
    714{
    715	.name = "DMIC16k Pin",
    716	.capture = {
    717		.channels_min = 1,
    718		.channels_max = 4,
    719	},
    720},
    721#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
    722{
    723	.name = "iDisp1 Pin",
    724	.playback = {
    725		.channels_min = 1,
    726		.channels_max = 8,
    727	},
    728},
    729{
    730	.name = "iDisp2 Pin",
    731	.playback = {
    732		.channels_min = 1,
    733		.channels_max = 8,
    734	},
    735},
    736{
    737	.name = "iDisp3 Pin",
    738	.playback = {
    739		.channels_min = 1,
    740		.channels_max = 8,
    741	},
    742},
    743{
    744	.name = "iDisp4 Pin",
    745	.playback = {
    746		.channels_min = 1,
    747		.channels_max = 8,
    748	},
    749},
    750{
    751	.name = "Analog CPU DAI",
    752	.playback = {
    753		.channels_min = 1,
    754		.channels_max = 16,
    755	},
    756	.capture = {
    757		.channels_min = 1,
    758		.channels_max = 16,
    759	},
    760},
    761{
    762	.name = "Digital CPU DAI",
    763	.playback = {
    764		.channels_min = 1,
    765		.channels_max = 16,
    766	},
    767	.capture = {
    768		.channels_min = 1,
    769		.channels_max = 16,
    770	},
    771},
    772{
    773	.name = "Alt Analog CPU DAI",
    774	.playback = {
    775		.channels_min = 1,
    776		.channels_max = 16,
    777	},
    778	.capture = {
    779		.channels_min = 1,
    780		.channels_max = 16,
    781	},
    782},
    783#endif
    784};
    785
    786int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
    787{
    788	/*
    789	 * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
    790	 * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
    791	 * Since the component suspend is called last, we can trap this corner case
    792	 * and force the DAIs to release their resources.
    793	 */
    794#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
    795	int ret;
    796
    797	ret = hda_dai_suspend(sof_to_bus(sdev));
    798	if (ret < 0)
    799		return ret;
    800#endif
    801
    802	return 0;
    803}