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