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