661 lines · c
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 or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2022 Intel Corporation7 8#include <sound/pcm_params.h>9#include <sound/hdaudio_ext.h>10#include <sound/hda_register.h>11#include <sound/hda-mlink.h>12#include <sound/sof/ipc4/header.h>13#include <uapi/sound/sof/header.h>14#include "../ipc4-priv.h"15#include "../ipc4-topology.h"16#include "../sof-priv.h"17#include "../sof-audio.h"18#include "hda.h"19 20/* These ops are only applicable for the HDA DAI's in their current form */21#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)22/*23 * This function checks if the host dma channel corresponding24 * to the link DMA stream_tag argument is assigned to one25 * of the FEs connected to the BE DAI.26 */27static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,28 int dir, int stream_tag)29{30 struct snd_pcm_substream *fe_substream;31 struct hdac_stream *fe_hstream;32 struct snd_soc_dpcm *dpcm;33 34 for_each_dpcm_fe(rtd, dir, dpcm) {35 fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);36 fe_hstream = fe_substream->runtime->private_data;37 if (fe_hstream->stream_tag == stream_tag)38 return true;39 }40 41 return false;42}43 44static struct hdac_ext_stream *45hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)46{47 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);48 struct sof_intel_hda_stream *hda_stream;49 const struct sof_intel_dsp_desc *chip;50 struct snd_sof_dev *sdev;51 struct hdac_ext_stream *res = NULL;52 struct hdac_stream *hstream = NULL;53 54 int stream_dir = substream->stream;55 56 if (!bus->ppcap) {57 dev_err(bus->dev, "stream type not supported\n");58 return NULL;59 }60 61 spin_lock_irq(&bus->reg_lock);62 list_for_each_entry(hstream, &bus->stream_list, list) {63 struct hdac_ext_stream *hext_stream =64 stream_to_hdac_ext_stream(hstream);65 if (hstream->direction != substream->stream)66 continue;67 68 hda_stream = hstream_to_sof_hda_stream(hext_stream);69 sdev = hda_stream->sdev;70 chip = get_chip_info(sdev->pdata);71 72 /* check if link is available */73 if (!hext_stream->link_locked) {74 /*75 * choose the first available link for platforms that do not have the76 * PROCEN_FMT_QUIRK set.77 */78 if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {79 res = hext_stream;80 break;81 }82 83 if (hstream->opened) {84 /*85 * check if the stream tag matches the stream86 * tag of one of the connected FEs87 */88 if (hda_check_fes(rtd, stream_dir,89 hstream->stream_tag)) {90 res = hext_stream;91 break;92 }93 } else {94 res = hext_stream;95 96 /*97 * This must be a hostless stream.98 * So reserve the host DMA channel.99 */100 hda_stream->host_reserved = 1;101 break;102 }103 }104 }105 106 if (res) {107 /* Make sure that host and link DMA is decoupled. */108 snd_hdac_ext_stream_decouple_locked(bus, res, true);109 110 res->link_locked = 1;111 res->link_substream = substream;112 }113 spin_unlock_irq(&bus->reg_lock);114 115 return res;116}117 118static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,119 struct snd_soc_dai *cpu_dai,120 struct snd_pcm_substream *substream)121{122 return snd_soc_dai_get_dma_data(cpu_dai, substream);123}124 125static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,126 struct snd_soc_dai *cpu_dai,127 struct snd_pcm_substream *substream)128{129 struct snd_sof_widget *pipe_widget;130 struct sof_ipc4_pipeline *pipeline;131 struct snd_sof_widget *swidget;132 struct snd_soc_dapm_widget *w;133 134 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);135 swidget = w->dobj.private;136 pipe_widget = swidget->spipe->pipe_widget;137 pipeline = pipe_widget->private;138 139 /* mark pipeline so that it can be skipped during FE trigger */140 pipeline->skip_during_fe_trigger = true;141 142 return snd_soc_dai_get_dma_data(cpu_dai, substream);143}144 145static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,146 struct snd_soc_dai *cpu_dai,147 struct snd_pcm_substream *substream)148{149 struct hdac_ext_stream *hext_stream;150 151 hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);152 if (!hext_stream)153 return NULL;154 155 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);156 157 return hext_stream;158}159 160static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,161 struct snd_pcm_substream *substream)162{163 struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);164 165 snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);166 snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);167}168 169static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,170 unsigned int format_val)171{172 snd_hdac_ext_stream_setup(hext_stream, format_val);173}174 175static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)176{177 snd_hdac_ext_stream_reset(hext_stream);178}179 180static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,181 struct snd_pcm_substream *substream,182 struct hdac_stream *hstream)183{184 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);185 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);186 187 /* set the hdac_stream in the codec dai */188 snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);189}190 191static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,192 struct snd_pcm_substream *substream,193 struct snd_pcm_hw_params *params)194{195 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);196 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);197 unsigned int link_bps;198 unsigned int format_val;199 unsigned int bits;200 201 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)202 link_bps = codec_dai->driver->playback.sig_bits;203 else204 link_bps = codec_dai->driver->capture.sig_bits;205 206 bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,207 link_bps);208 format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));209 210 dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,211 params_rate(params), params_channels(params), params_format(params));212 213 return format_val;214}215 216static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,217 struct snd_pcm_substream *substream)218{219 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);220 struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);221 struct hdac_bus *bus = sof_to_bus(sdev);222 223 return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);224}225 226static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,227 struct snd_pcm_substream *substream,228 struct snd_pcm_hw_params *params)229{230 unsigned int format_val;231 unsigned int bits;232 233 bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,234 params_physical_width(params));235 format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));236 237 dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,238 params_rate(params), params_channels(params), params_format(params));239 240 return format_val;241}242 243static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,244 struct snd_pcm_substream *substream,245 struct snd_pcm_hw_params *params)246{247 unsigned int format_val;248 snd_pcm_format_t format;249 unsigned int channels;250 unsigned int width;251 unsigned int bits;252 253 channels = params_channels(params);254 format = params_format(params);255 width = params_physical_width(params);256 257 if (format == SNDRV_PCM_FORMAT_S16_LE) {258 format = SNDRV_PCM_FORMAT_S32_LE;259 channels /= 2;260 width = 32;261 }262 263 bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width);264 format_val = snd_hdac_stream_format(channels, bits, params_rate(params));265 266 dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,267 params_rate(params), channels, format);268 269 return format_val;270}271 272static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev,273 struct snd_pcm_substream *substream)274{275 struct hdac_bus *bus = sof_to_bus(sdev);276 277 return hdac_bus_eml_ssp_get_hlink(bus);278}279 280static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev,281 struct snd_pcm_substream *substream)282{283 struct hdac_bus *bus = sof_to_bus(sdev);284 285 return hdac_bus_eml_dmic_get_hlink(bus);286}287 288static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,289 struct snd_pcm_substream *substream)290{291 struct hdac_bus *bus = sof_to_bus(sdev);292 293 return hdac_bus_eml_sdw_get_hlink(bus);294}295 296static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,297 struct snd_pcm_substream *substream, int cmd)298{299 struct sof_ipc4_fw_data *ipc4_data = sdev->private;300 struct snd_sof_widget *pipe_widget;301 struct sof_ipc4_pipeline *pipeline;302 struct snd_sof_widget *swidget;303 struct snd_soc_dapm_widget *w;304 int ret = 0;305 306 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);307 swidget = w->dobj.private;308 pipe_widget = swidget->spipe->pipe_widget;309 pipeline = pipe_widget->private;310 311 if (pipe_widget->instance_id < 0)312 return 0;313 314 mutex_lock(&ipc4_data->pipeline_state_mutex);315 316 switch (cmd) {317 case SNDRV_PCM_TRIGGER_START:318 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:319 break;320 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:321 case SNDRV_PCM_TRIGGER_SUSPEND:322 case SNDRV_PCM_TRIGGER_STOP:323 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,324 SOF_IPC4_PIPE_PAUSED);325 if (ret < 0)326 goto out;327 328 pipeline->state = SOF_IPC4_PIPE_PAUSED;329 break;330 default:331 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);332 ret = -EINVAL;333 }334out:335 mutex_unlock(&ipc4_data->pipeline_state_mutex);336 return ret;337}338 339static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,340 struct snd_pcm_substream *substream, int cmd)341{342 struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);343 344 switch (cmd) {345 case SNDRV_PCM_TRIGGER_START:346 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:347 snd_hdac_ext_stream_start(hext_stream);348 break;349 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:350 /*351 * Save the LLP registers since in case of PAUSE the LLP352 * register are not reset to 0, the delay calculation will use353 * the saved offsets for compensating the delay calculation.354 */355 hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);356 hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);357 snd_hdac_ext_stream_clear(hext_stream);358 break;359 case SNDRV_PCM_TRIGGER_SUSPEND:360 case SNDRV_PCM_TRIGGER_STOP:361 hext_stream->pplcllpl = 0;362 hext_stream->pplcllpu = 0;363 snd_hdac_ext_stream_clear(hext_stream);364 break;365 default:366 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);367 return -EINVAL;368 }369 370 return 0;371}372 373static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,374 struct snd_pcm_substream *substream, int cmd)375{376 struct sof_ipc4_fw_data *ipc4_data = sdev->private;377 struct snd_sof_widget *pipe_widget;378 struct sof_ipc4_pipeline *pipeline;379 struct snd_sof_widget *swidget;380 struct snd_soc_dapm_widget *w;381 int ret = 0;382 383 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);384 swidget = w->dobj.private;385 pipe_widget = swidget->spipe->pipe_widget;386 pipeline = pipe_widget->private;387 388 if (pipe_widget->instance_id < 0)389 return 0;390 391 mutex_lock(&ipc4_data->pipeline_state_mutex);392 393 switch (cmd) {394 case SNDRV_PCM_TRIGGER_START:395 if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {396 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,397 SOF_IPC4_PIPE_PAUSED);398 if (ret < 0)399 goto out;400 pipeline->state = SOF_IPC4_PIPE_PAUSED;401 }402 403 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,404 SOF_IPC4_PIPE_RUNNING);405 if (ret < 0)406 goto out;407 pipeline->state = SOF_IPC4_PIPE_RUNNING;408 swidget->spipe->started_count++;409 break;410 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:411 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,412 SOF_IPC4_PIPE_RUNNING);413 if (ret < 0)414 goto out;415 pipeline->state = SOF_IPC4_PIPE_RUNNING;416 break;417 case SNDRV_PCM_TRIGGER_SUSPEND:418 case SNDRV_PCM_TRIGGER_STOP:419 /*420 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have421 * been stopped. So, clear the started_count so that the pipeline can be reset422 */423 swidget->spipe->started_count = 0;424 break;425 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:426 break;427 default:428 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);429 ret = -EINVAL;430 break;431 }432out:433 mutex_unlock(&ipc4_data->pipeline_state_mutex);434 return ret;435}436 437static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {438 .get_hext_stream = hda_ipc4_get_hext_stream,439 .assign_hext_stream = hda_assign_hext_stream,440 .release_hext_stream = hda_release_hext_stream,441 .setup_hext_stream = hda_setup_hext_stream,442 .reset_hext_stream = hda_reset_hext_stream,443 .pre_trigger = hda_ipc4_pre_trigger,444 .trigger = hda_trigger,445 .post_trigger = hda_ipc4_post_trigger,446 .codec_dai_set_stream = hda_codec_dai_set_stream,447 .calc_stream_format = hda_calc_stream_format,448 .get_hlink = hda_get_hlink,449};450 451static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = {452 .get_hext_stream = hda_ipc4_get_hext_stream,453 .assign_hext_stream = hda_assign_hext_stream,454 .release_hext_stream = hda_release_hext_stream,455 .setup_hext_stream = hda_setup_hext_stream,456 .reset_hext_stream = hda_reset_hext_stream,457 .pre_trigger = hda_ipc4_pre_trigger,458 .trigger = hda_trigger,459 .post_trigger = hda_ipc4_post_trigger,460 .calc_stream_format = generic_calc_stream_format,461 .get_hlink = ssp_get_hlink,462};463 464static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {465 .get_hext_stream = hda_ipc4_get_hext_stream,466 .assign_hext_stream = hda_assign_hext_stream,467 .release_hext_stream = hda_release_hext_stream,468 .setup_hext_stream = hda_setup_hext_stream,469 .reset_hext_stream = hda_reset_hext_stream,470 .pre_trigger = hda_ipc4_pre_trigger,471 .trigger = hda_trigger,472 .post_trigger = hda_ipc4_post_trigger,473 .calc_stream_format = dmic_calc_stream_format,474 .get_hlink = dmic_get_hlink,475};476 477static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {478 .get_hext_stream = hda_ipc4_get_hext_stream,479 .assign_hext_stream = hda_assign_hext_stream,480 .release_hext_stream = hda_release_hext_stream,481 .setup_hext_stream = hda_setup_hext_stream,482 .reset_hext_stream = hda_reset_hext_stream,483 .pre_trigger = hda_ipc4_pre_trigger,484 .trigger = hda_trigger,485 .post_trigger = hda_ipc4_post_trigger,486 .calc_stream_format = generic_calc_stream_format,487 .get_hlink = sdw_get_hlink,488};489 490static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {491 .get_hext_stream = hda_get_hext_stream,492 .assign_hext_stream = hda_assign_hext_stream,493 .release_hext_stream = hda_release_hext_stream,494 .setup_hext_stream = hda_setup_hext_stream,495 .reset_hext_stream = hda_reset_hext_stream,496 .trigger = hda_trigger,497 .codec_dai_set_stream = hda_codec_dai_set_stream,498 .calc_stream_format = hda_calc_stream_format,499 .get_hlink = hda_get_hlink,500};501 502static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {503 .get_hext_stream = hda_get_hext_stream,504 .assign_hext_stream = hda_assign_hext_stream,505 .release_hext_stream = hda_release_hext_stream,506 .setup_hext_stream = hda_setup_hext_stream,507 .reset_hext_stream = hda_reset_hext_stream,508 .trigger = hda_trigger,509 .calc_stream_format = generic_calc_stream_format,510 .get_hlink = sdw_get_hlink,511};512 513static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,514 struct snd_pcm_substream *substream, int cmd)515{516 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);517 518 switch (cmd) {519 case SNDRV_PCM_TRIGGER_SUSPEND:520 case SNDRV_PCM_TRIGGER_STOP:521 {522 struct snd_sof_dai_config_data data = { 0 };523 int ret;524 525 data.dai_data = DMA_CHAN_INVALID;526 ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);527 if (ret < 0)528 return ret;529 530 break;531 }532 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:533 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);534 default:535 break;536 }537 538 return 0;539}540 541static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {542 .get_hext_stream = hda_get_hext_stream,543 .assign_hext_stream = hda_assign_hext_stream,544 .release_hext_stream = hda_release_hext_stream,545 .setup_hext_stream = hda_setup_hext_stream,546 .reset_hext_stream = hda_reset_hext_stream,547 .trigger = hda_trigger,548 .post_trigger = hda_ipc3_post_trigger,549 .codec_dai_set_stream = hda_codec_dai_set_stream,550 .calc_stream_format = hda_calc_stream_format,551 .get_hlink = hda_get_hlink,552};553 554static struct hdac_ext_stream *555hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,556 struct snd_pcm_substream *substream)557{558 struct hdac_stream *hstream = substream->runtime->private_data;559 560 return stream_to_hdac_ext_stream(hstream);561}562 563static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,564 struct hdac_ext_stream *hext_stream,565 unsigned int format_val)566{567 /*568 * Save the format_val which was adjusted by the maxbps of the codec.569 * This information is not available on the FE side since there we are570 * using dummy_codec.571 */572 hext_stream->hstream.format_val = format_val;573}574 575static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {576 .get_hext_stream = hda_dspless_get_hext_stream,577 .setup_hext_stream = hda_dspless_setup_hext_stream,578 .codec_dai_set_stream = hda_codec_dai_set_stream,579 .calc_stream_format = hda_calc_stream_format,580 .get_hlink = hda_get_hlink,581};582 583static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {584 .get_hext_stream = hda_dspless_get_hext_stream,585 .setup_hext_stream = hda_dspless_setup_hext_stream,586 .calc_stream_format = generic_calc_stream_format,587 .get_hlink = sdw_get_hlink,588};589 590#endif591 592const struct hda_dai_widget_dma_ops *593hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)594{595#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)596 struct snd_sof_dai *sdai;597 const struct sof_intel_dsp_desc *chip;598 599 chip = get_chip_info(sdev->pdata);600 sdai = swidget->private;601 602 if (sdev->dspless_mode_selected) {603 switch (sdai->type) {604 case SOF_DAI_INTEL_HDA:605 return &hda_dspless_dma_ops;606 case SOF_DAI_INTEL_ALH:607 if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)608 return NULL;609 return &sdw_dspless_dma_ops;610 default:611 return NULL;612 }613 }614 615 switch (sdev->pdata->ipc_type) {616 case SOF_IPC_TYPE_3:617 {618 struct sof_dai_private_data *private = sdai->private;619 620 if (private->dai_config->type == SOF_DAI_INTEL_HDA)621 return &hda_ipc3_dma_ops;622 break;623 }624 case SOF_IPC_TYPE_4:625 {626 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;627 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;628 629 switch (sdai->type) {630 case SOF_DAI_INTEL_HDA:631 if (pipeline->use_chain_dma)632 return &hda_ipc4_chain_dma_ops;633 634 return &hda_ipc4_dma_ops;635 case SOF_DAI_INTEL_SSP:636 if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)637 return NULL;638 return &ssp_ipc4_dma_ops;639 case SOF_DAI_INTEL_DMIC:640 if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)641 return NULL;642 return &dmic_ipc4_dma_ops;643 case SOF_DAI_INTEL_ALH:644 if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)645 return NULL;646 if (pipeline->use_chain_dma)647 return &sdw_ipc4_chain_dma_ops;648 return &sdw_ipc4_dma_ops;649 650 default:651 break;652 }653 break;654 }655 default:656 break;657 }658#endif659 return NULL;660}661