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}