linux/sound/soc/soc-dai.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// soc-dai.c
   4//
   5// Copyright (C) 2019 Renesas Electronics Corp.
   6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
   7//
   8
   9#include <sound/soc.h>
  10#include <sound/soc-dai.h>
  11#include <sound/soc-link.h>
  12
  13#define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret)
  14static inline int _soc_dai_ret(const struct snd_soc_dai *dai,
  15                               const char *func, int ret)
  16{
  17        return snd_soc_ret(dai->dev, ret,
  18                           "at %s() on %s\n", func, dai->name);
  19}
  20
  21/*
  22 * We might want to check substream by using list.
  23 * In such case, we can update these macros.
  24 */
  25#define soc_dai_mark_push(dai, substream, tgt)  ((dai)->mark_##tgt = substream)
  26#define soc_dai_mark_pop(dai, tgt)      ((dai)->mark_##tgt = NULL)
  27#define soc_dai_mark_match(dai, substream, tgt) ((dai)->mark_##tgt == substream)
  28
  29/**
  30 * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  31 * @dai: DAI
  32 * @clk_id: DAI specific clock ID
  33 * @freq: new clock frequency in Hz
  34 * @dir: new clock direction (SND_SOC_CLOCK_IN or SND_SOC_CLOCK_OUT)
  35 *
  36 * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
  37 */
  38int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
  39                           unsigned int freq, int dir)
  40{
  41        int ret;
  42
  43        if (dai->driver->ops &&
  44            dai->driver->ops->set_sysclk)
  45                ret = dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
  46        else
  47                ret = snd_soc_component_set_sysclk(dai->component, clk_id, 0,
  48                                                   freq, dir);
  49
  50        return soc_dai_ret(dai, ret);
  51}
  52EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
  53
  54/**
  55 * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
  56 * @dai: DAI
  57 * @div_id: DAI specific clock divider ID
  58 * @div: new clock divisor.
  59 *
  60 * Configures the clock dividers. This is used to derive the best DAI bit and
  61 * frame clocks from the system or master clock. It's best to set the DAI bit
  62 * and frame clocks as low as possible to save system power.
  63 */
  64int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
  65                           int div_id, int div)
  66{
  67        int ret = -EINVAL;
  68
  69        if (dai->driver->ops &&
  70            dai->driver->ops->set_clkdiv)
  71                ret = dai->driver->ops->set_clkdiv(dai, div_id, div);
  72
  73        return soc_dai_ret(dai, ret);
  74}
  75EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
  76
  77/**
  78 * snd_soc_dai_set_pll - configure DAI PLL.
  79 * @dai: DAI
  80 * @pll_id: DAI specific PLL ID
  81 * @source: DAI specific source for the PLL
  82 * @freq_in: PLL input clock frequency in Hz
  83 * @freq_out: requested PLL output clock frequency in Hz
  84 *
  85 * Configures and enables PLL to generate output clock based on input clock.
  86 */
  87int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
  88                        unsigned int freq_in, unsigned int freq_out)
  89{
  90        int ret;
  91
  92        if (dai->driver->ops &&
  93            dai->driver->ops->set_pll)
  94                ret = dai->driver->ops->set_pll(dai, pll_id, source,
  95                                                freq_in, freq_out);
  96        else
  97                ret = snd_soc_component_set_pll(dai->component, pll_id, source,
  98                                                freq_in, freq_out);
  99
 100        return soc_dai_ret(dai, ret);
 101}
 102EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 103
 104/**
 105 * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio.
 106 * @dai: DAI
 107 * @ratio: Ratio of BCLK to Sample rate.
 108 *
 109 * Configures the DAI for a preset BCLK to sample rate ratio.
 110 */
 111int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
 112{
 113        int ret = -ENOTSUPP;
 114
 115        if (dai->driver->ops &&
 116            dai->driver->ops->set_bclk_ratio)
 117                ret = dai->driver->ops->set_bclk_ratio(dai, ratio);
 118
 119        return soc_dai_ret(dai, ret);
 120}
 121EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio);
 122
 123int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd)
 124{
 125        struct snd_soc_dai *dai;
 126        int i, max = 0;
 127
 128        /*
 129         * return max num if *ALL* DAIs have .auto_selectable_formats
 130         */
 131        for_each_rtd_dais(rtd, i, dai) {
 132                if (dai->driver->ops &&
 133                    dai->driver->ops->num_auto_selectable_formats)
 134                        max = max(max, dai->driver->ops->num_auto_selectable_formats);
 135                else
 136                        return 0;
 137        }
 138
 139        return max;
 140}
 141
 142/**
 143 * snd_soc_dai_get_fmt - get supported audio format.
 144 * @dai: DAI
 145 * @priority: priority level of supported audio format.
 146 *
 147 * This should return only formats implemented with high
 148 * quality by the DAI so that the core can configure a
 149 * format which will work well with other devices.
 150 * For example devices which don't support both edges of the
 151 * LRCLK signal in I2S style formats should only list DSP
 152 * modes.  This will mean that sometimes fewer formats
 153 * are reported here than are supported by set_fmt().
 154 */
 155u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority)
 156{
 157        const struct snd_soc_dai_ops *ops = dai->driver->ops;
 158        u64 fmt = 0;
 159        int i, max = 0, until = priority;
 160
 161        /*
 162         * Collect auto_selectable_formats until priority
 163         *
 164         * ex)
 165         *      auto_selectable_formats[] = { A, B, C };
 166         *      (A, B, C = SND_SOC_POSSIBLE_DAIFMT_xxx)
 167         *
 168         * priority = 1 :       A
 169         * priority = 2 :       A | B
 170         * priority = 3 :       A | B | C
 171         * priority = 4 :       A | B | C
 172         * ...
 173         */
 174        if (ops)
 175                max = ops->num_auto_selectable_formats;
 176
 177        if (max < until)
 178                until = max;
 179
 180        for (i = 0; i < until; i++)
 181                fmt |= ops->auto_selectable_formats[i];
 182
 183        return fmt;
 184}
 185
 186/**
 187 * snd_soc_dai_set_fmt - configure DAI hardware audio format.
 188 * @dai: DAI
 189 * @fmt: SND_SOC_DAIFMT_* format value.
 190 *
 191 * Configures the DAI hardware format and clocking.
 192 */
 193int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 194{
 195        int ret = -ENOTSUPP;
 196
 197        if (dai->driver->ops && dai->driver->ops->set_fmt)
 198                ret = dai->driver->ops->set_fmt(dai, fmt);
 199
 200        return soc_dai_ret(dai, ret);
 201}
 202EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 203
 204/**
 205 * snd_soc_xlate_tdm_slot_mask - generate tx/rx slot mask.
 206 * @slots: Number of slots in use.
 207 * @tx_mask: bitmask representing active TX slots.
 208 * @rx_mask: bitmask representing active RX slots.
 209 *
 210 * Generates the TDM tx and rx slot default masks for DAI.
 211 */
 212static int snd_soc_xlate_tdm_slot_mask(unsigned int slots,
 213                                       unsigned int *tx_mask,
 214                                       unsigned int *rx_mask)
 215{
 216        if (*tx_mask || *rx_mask)
 217                return 0;
 218
 219        if (!slots)
 220                return -EINVAL;
 221
 222        *tx_mask = (1 << slots) - 1;
 223        *rx_mask = (1 << slots) - 1;
 224
 225        return 0;
 226}
 227
 228/**
 229 * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation
 230 * @dai: The DAI to configure
 231 * @tx_mask: bitmask representing active TX slots.
 232 * @rx_mask: bitmask representing active RX slots.
 233 * @slots: Number of slots in use.
 234 * @slot_width: Width in bits for each slot.
 235 *
 236 * This function configures the specified DAI for TDM operation. @slot contains
 237 * the total number of slots of the TDM stream and @slot_with the width of each
 238 * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the
 239 * active slots of the TDM stream for the specified DAI, i.e. which slots the
 240 * DAI should write to or read from. If a bit is set the corresponding slot is
 241 * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to
 242 * the first slot, bit 1 to the second slot and so on. The first active slot
 243 * maps to the first channel of the DAI, the second active slot to the second
 244 * channel and so on.
 245 *
 246 * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask,
 247 * @rx_mask and @slot_width will be ignored.
 248 *
 249 * Returns 0 on success, a negative error code otherwise.
 250 */
 251int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 252                             unsigned int tx_mask, unsigned int rx_mask,
 253                             int slots, int slot_width)
 254{
 255        int ret = -ENOTSUPP;
 256        int stream;
 257        unsigned int *tdm_mask[] = {
 258                &tx_mask,
 259                &rx_mask,
 260        };
 261
 262        if (slots) {
 263                if (dai->driver->ops &&
 264                    dai->driver->ops->xlate_tdm_slot_mask)
 265                        ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
 266                else
 267                        ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
 268                if (ret)
 269                        goto err;
 270        }
 271
 272        for_each_pcm_streams(stream)
 273                snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]);
 274
 275        if (dai->driver->ops &&
 276            dai->driver->ops->set_tdm_slot)
 277                ret = dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
 278                                                      slots, slot_width);
 279err:
 280        return soc_dai_ret(dai, ret);
 281}
 282EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 283
 284/**
 285 * snd_soc_dai_set_channel_map - configure DAI audio channel map
 286 * @dai: DAI
 287 * @tx_num: how many TX channels
 288 * @tx_slot: pointer to an array which imply the TX slot number channel
 289 *           0~num-1 uses
 290 * @rx_num: how many RX channels
 291 * @rx_slot: pointer to an array which imply the RX slot number channel
 292 *           0~num-1 uses
 293 *
 294 * configure the relationship between channel number and TDM slot number.
 295 */
 296int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
 297                                unsigned int tx_num, const unsigned int *tx_slot,
 298                                unsigned int rx_num, const unsigned int *rx_slot)
 299{
 300        int ret = -ENOTSUPP;
 301
 302        if (dai->driver->ops &&
 303            dai->driver->ops->set_channel_map)
 304                ret = dai->driver->ops->set_channel_map(dai, tx_num, tx_slot,
 305                                                        rx_num, rx_slot);
 306        return soc_dai_ret(dai, ret);
 307}
 308EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
 309
 310/**
 311 * snd_soc_dai_get_channel_map - Get DAI audio channel map
 312 * @dai: DAI
 313 * @tx_num: how many TX channels
 314 * @tx_slot: pointer to an array which imply the TX slot number channel
 315 *           0~num-1 uses
 316 * @rx_num: how many RX channels
 317 * @rx_slot: pointer to an array which imply the RX slot number channel
 318 *           0~num-1 uses
 319 */
 320int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai,
 321                                unsigned int *tx_num, unsigned int *tx_slot,
 322                                unsigned int *rx_num, unsigned int *rx_slot)
 323{
 324        int ret = -ENOTSUPP;
 325
 326        if (dai->driver->ops &&
 327            dai->driver->ops->get_channel_map)
 328                ret = dai->driver->ops->get_channel_map(dai, tx_num, tx_slot,
 329                                                        rx_num, rx_slot);
 330        return soc_dai_ret(dai, ret);
 331}
 332EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map);
 333
 334/**
 335 * snd_soc_dai_set_tristate - configure DAI system or master clock.
 336 * @dai: DAI
 337 * @tristate: tristate enable
 338 *
 339 * Tristates the DAI so that others can use it.
 340 */
 341int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 342{
 343        int ret = -EINVAL;
 344
 345        if (dai->driver->ops &&
 346            dai->driver->ops->set_tristate)
 347                ret = dai->driver->ops->set_tristate(dai, tristate);
 348
 349        return soc_dai_ret(dai, ret);
 350}
 351EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
 352
 353int snd_soc_dai_prepare(struct snd_soc_dai *dai,
 354                        struct snd_pcm_substream *substream)
 355{
 356        int ret = 0;
 357
 358        if (!snd_soc_dai_stream_valid(dai, substream->stream))
 359                return 0;
 360
 361        if (dai->driver->ops &&
 362            dai->driver->ops->prepare)
 363                ret = dai->driver->ops->prepare(substream, dai);
 364
 365        return soc_dai_ret(dai, ret);
 366}
 367EXPORT_SYMBOL_GPL(snd_soc_dai_prepare);
 368
 369int snd_soc_dai_mute_is_ctrled_at_trigger(struct snd_soc_dai *dai)
 370{
 371        if (dai->driver->ops)
 372                return dai->driver->ops->mute_unmute_on_trigger;
 373
 374        return 0;
 375}
 376
 377/**
 378 * snd_soc_dai_digital_mute - configure DAI system or master clock.
 379 * @dai: DAI
 380 * @mute: mute enable
 381 * @direction: stream to mute
 382 *
 383 * Mutes the DAI DAC.
 384 */
 385int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
 386                             int direction)
 387{
 388        int ret = -ENOTSUPP;
 389
 390        /*
 391         * ignore if direction was CAPTURE
 392         * and it had .no_capture_mute flag
 393         */
 394        if (dai->driver->ops &&
 395            dai->driver->ops->mute_stream &&
 396            (direction == SNDRV_PCM_STREAM_PLAYBACK ||
 397             !dai->driver->ops->no_capture_mute))
 398                ret = dai->driver->ops->mute_stream(dai, mute, direction);
 399
 400        return soc_dai_ret(dai, ret);
 401}
 402EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 403
 404int snd_soc_dai_hw_params(struct snd_soc_dai *dai,
 405                          struct snd_pcm_substream *substream,
 406                          struct snd_pcm_hw_params *params)
 407{
 408        int ret = 0;
 409
 410        if (dai->driver->ops &&
 411            dai->driver->ops->hw_params)
 412                ret = dai->driver->ops->hw_params(substream, params, dai);
 413
 414        /* mark substream if succeeded */
 415        if (ret == 0)
 416                soc_dai_mark_push(dai, substream, hw_params);
 417
 418        return soc_dai_ret(dai, ret);
 419}
 420
 421void snd_soc_dai_hw_free(struct snd_soc_dai *dai,
 422                         struct snd_pcm_substream *substream,
 423                         int rollback)
 424{
 425        if (rollback && !soc_dai_mark_match(dai, substream, hw_params))
 426                return;
 427
 428        if (dai->driver->ops &&
 429            dai->driver->ops->hw_free)
 430                dai->driver->ops->hw_free(substream, dai);
 431
 432        /* remove marked substream */
 433        soc_dai_mark_pop(dai, hw_params);
 434}
 435
 436int snd_soc_dai_startup(struct snd_soc_dai *dai,
 437                        struct snd_pcm_substream *substream)
 438{
 439        int ret = 0;
 440
 441        if (!snd_soc_dai_stream_valid(dai, substream->stream))
 442                return 0;
 443
 444        if (dai->driver->ops &&
 445            dai->driver->ops->startup)
 446                ret = dai->driver->ops->startup(substream, dai);
 447
 448        /* mark substream if succeeded */
 449        if (ret == 0)
 450                soc_dai_mark_push(dai, substream, startup);
 451
 452        return soc_dai_ret(dai, ret);
 453}
 454
 455void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
 456                          struct snd_pcm_substream *substream,
 457                          int rollback)
 458{
 459        if (!snd_soc_dai_stream_valid(dai, substream->stream))
 460                return;
 461
 462        if (rollback && !soc_dai_mark_match(dai, substream, startup))
 463                return;
 464
 465        if (dai->driver->ops &&
 466            dai->driver->ops->shutdown)
 467                dai->driver->ops->shutdown(substream, dai);
 468
 469        /* remove marked substream */
 470        soc_dai_mark_pop(dai, startup);
 471}
 472
 473int snd_soc_dai_compress_new(struct snd_soc_dai *dai,
 474                             struct snd_soc_pcm_runtime *rtd)
 475{
 476        int ret = -ENOTSUPP;
 477        if (dai->driver->ops &&
 478            dai->driver->ops->compress_new)
 479                ret = dai->driver->ops->compress_new(rtd);
 480        return soc_dai_ret(dai, ret);
 481}
 482
 483/*
 484 * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
 485 *
 486 * Returns true if the DAI supports the indicated stream type.
 487 */
 488bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int dir)
 489{
 490        const struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir);
 491
 492        /* If the codec specifies any channels at all, it supports the stream */
 493        return stream->channels_min;
 494}
 495
 496void snd_soc_dai_action(struct snd_soc_dai *dai,
 497                        int stream, int action)
 498{
 499        /* see snd_soc_dai_stream_active() */
 500        dai->stream[stream].active      += action;
 501
 502        /* see snd_soc_component_active() */
 503        dai->component->active          += action;
 504}
 505EXPORT_SYMBOL_GPL(snd_soc_dai_action);
 506
 507int snd_soc_dai_active(const struct snd_soc_dai *dai)
 508{
 509        int stream, active;
 510
 511        active = 0;
 512        for_each_pcm_streams(stream)
 513                active += dai->stream[stream].active;
 514
 515        return active;
 516}
 517EXPORT_SYMBOL_GPL(snd_soc_dai_active);
 518
 519int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order)
 520{
 521        struct snd_soc_dai *dai;
 522        int i;
 523
 524        for_each_rtd_dais(rtd, i, dai) {
 525                if (dai->probed)
 526                        continue;
 527
 528                if (dai->driver->ops) {
 529                        if (dai->driver->ops->probe_order != order)
 530                                continue;
 531
 532                        if (dai->driver->ops->probe) {
 533                                int ret = dai->driver->ops->probe(dai);
 534
 535                                if (ret < 0)
 536                                        return soc_dai_ret(dai, ret);
 537                        }
 538                }
 539                dai->probed = 1;
 540        }
 541
 542        return 0;
 543}
 544
 545int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order)
 546{
 547        struct snd_soc_dai *dai;
 548        int i, r, ret = 0;
 549
 550        for_each_rtd_dais(rtd, i, dai) {
 551                if (!dai->probed)
 552                        continue;
 553
 554                if (dai->driver->ops) {
 555                        if (dai->driver->ops->remove_order != order)
 556                                continue;
 557
 558                        if (dai->driver->ops->remove) {
 559                                r = dai->driver->ops->remove(dai);
 560                                if (r < 0)
 561                                        ret = r; /* use last error */
 562                        }
 563                }
 564                dai->probed = 0;
 565        }
 566
 567        return ret;
 568}
 569
 570int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd)
 571{
 572        struct snd_soc_dai *dai;
 573        int i;
 574
 575        for_each_rtd_dais(rtd, i, dai) {
 576                if (dai->driver->ops &&
 577                    dai->driver->ops->pcm_new) {
 578                        int ret = dai->driver->ops->pcm_new(rtd, dai);
 579                        if (ret < 0)
 580                                return soc_dai_ret(dai, ret);
 581                }
 582        }
 583
 584        return 0;
 585}
 586
 587int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream)
 588{
 589        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 590        struct snd_soc_dai *dai;
 591        int i, ret;
 592
 593        for_each_rtd_dais(rtd, i, dai) {
 594                ret = snd_soc_dai_prepare(dai, substream);
 595                if (ret < 0)
 596                        return ret;
 597        }
 598
 599        return 0;
 600}
 601
 602static int soc_dai_trigger(struct snd_soc_dai *dai,
 603                           struct snd_pcm_substream *substream, int cmd)
 604{
 605        int ret = 0;
 606
 607        if (!snd_soc_dai_stream_valid(dai, substream->stream))
 608                return 0;
 609
 610        if (dai->driver->ops &&
 611            dai->driver->ops->trigger)
 612                ret = dai->driver->ops->trigger(substream, cmd, dai);
 613
 614        return soc_dai_ret(dai, ret);
 615}
 616
 617int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream,
 618                            int cmd, int rollback)
 619{
 620        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 621        struct snd_soc_dai *dai;
 622        int i, r, ret = 0;
 623
 624        switch (cmd) {
 625        case SNDRV_PCM_TRIGGER_START:
 626        case SNDRV_PCM_TRIGGER_RESUME:
 627        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 628                for_each_rtd_dais(rtd, i, dai) {
 629                        ret = soc_dai_trigger(dai, substream, cmd);
 630                        if (ret < 0)
 631                                break;
 632
 633                        if (snd_soc_dai_mute_is_ctrled_at_trigger(dai))
 634                                snd_soc_dai_digital_mute(dai, 0, substream->stream);
 635
 636                        soc_dai_mark_push(dai, substream, trigger);
 637                }
 638                break;
 639        case SNDRV_PCM_TRIGGER_STOP:
 640        case SNDRV_PCM_TRIGGER_SUSPEND:
 641        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 642                for_each_rtd_dais(rtd, i, dai) {
 643                        if (rollback && !soc_dai_mark_match(dai, substream, trigger))
 644                                continue;
 645
 646                        if (snd_soc_dai_mute_is_ctrled_at_trigger(dai))
 647                                snd_soc_dai_digital_mute(dai, 1, substream->stream);
 648
 649                        r = soc_dai_trigger(dai, substream, cmd);
 650                        if (r < 0)
 651                                ret = r; /* use last ret */
 652                        soc_dai_mark_pop(dai, trigger);
 653                }
 654        }
 655
 656        return ret;
 657}
 658
 659void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream,
 660                           snd_pcm_sframes_t *cpu_delay,
 661                           snd_pcm_sframes_t *codec_delay)
 662{
 663        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 664        struct snd_soc_dai *dai;
 665        int i;
 666
 667        /*
 668         * We're looking for the delay through the full audio path so it needs to
 669         * be the maximum of the DAIs doing transmit and the maximum of the DAIs
 670         * doing receive (ie, all CPUs and all CODECs) rather than just the maximum
 671         * of all DAIs.
 672         */
 673
 674        /* for CPU */
 675        for_each_rtd_cpu_dais(rtd, i, dai)
 676                if (dai->driver->ops &&
 677                    dai->driver->ops->delay)
 678                        *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai));
 679
 680        /* for Codec */
 681        for_each_rtd_codec_dais(rtd, i, dai)
 682                if (dai->driver->ops &&
 683                    dai->driver->ops->delay)
 684                        *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai));
 685}
 686
 687int snd_soc_dai_compr_startup(struct snd_soc_dai *dai,
 688                              struct snd_compr_stream *cstream)
 689{
 690        int ret = 0;
 691
 692        if (dai->driver->cops &&
 693            dai->driver->cops->startup)
 694                ret = dai->driver->cops->startup(cstream, dai);
 695
 696        /* mark cstream if succeeded */
 697        if (ret == 0)
 698                soc_dai_mark_push(dai, cstream, compr_startup);
 699
 700        return soc_dai_ret(dai, ret);
 701}
 702EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup);
 703
 704void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai,
 705                                struct snd_compr_stream *cstream,
 706                                int rollback)
 707{
 708        if (rollback && !soc_dai_mark_match(dai, cstream, compr_startup))
 709                return;
 710
 711        if (dai->driver->cops &&
 712            dai->driver->cops->shutdown)
 713                dai->driver->cops->shutdown(cstream, dai);
 714
 715        /* remove marked cstream */
 716        soc_dai_mark_pop(dai, compr_startup);
 717}
 718EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown);
 719
 720int snd_soc_dai_compr_trigger(struct snd_soc_dai *dai,
 721                              struct snd_compr_stream *cstream, int cmd)
 722{
 723        int ret = 0;
 724
 725        if (dai->driver->cops &&
 726            dai->driver->cops->trigger)
 727                ret = dai->driver->cops->trigger(cstream, cmd, dai);
 728
 729        return soc_dai_ret(dai, ret);
 730}
 731EXPORT_SYMBOL_GPL(snd_soc_dai_compr_trigger);
 732
 733int snd_soc_dai_compr_set_params(struct snd_soc_dai *dai,
 734                                 struct snd_compr_stream *cstream,
 735                                 struct snd_compr_params *params)
 736{
 737        int ret = 0;
 738
 739        if (dai->driver->cops &&
 740            dai->driver->cops->set_params)
 741                ret = dai->driver->cops->set_params(cstream, params, dai);
 742
 743        return soc_dai_ret(dai, ret);
 744}
 745EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_params);
 746
 747int snd_soc_dai_compr_get_params(struct snd_soc_dai *dai,
 748                                 struct snd_compr_stream *cstream,
 749                                 struct snd_codec *params)
 750{
 751        int ret = 0;
 752
 753        if (dai->driver->cops &&
 754            dai->driver->cops->get_params)
 755                ret = dai->driver->cops->get_params(cstream, params, dai);
 756
 757        return soc_dai_ret(dai, ret);
 758}
 759EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_params);
 760
 761int snd_soc_dai_compr_ack(struct snd_soc_dai *dai,
 762                          struct snd_compr_stream *cstream,
 763                          size_t bytes)
 764{
 765        int ret = 0;
 766
 767        if (dai->driver->cops &&
 768            dai->driver->cops->ack)
 769                ret = dai->driver->cops->ack(cstream, bytes, dai);
 770
 771        return soc_dai_ret(dai, ret);
 772}
 773EXPORT_SYMBOL_GPL(snd_soc_dai_compr_ack);
 774
 775int snd_soc_dai_compr_pointer(struct snd_soc_dai *dai,
 776                              struct snd_compr_stream *cstream,
 777                              struct snd_compr_tstamp *tstamp)
 778{
 779        int ret = 0;
 780
 781        if (dai->driver->cops &&
 782            dai->driver->cops->pointer)
 783                ret = dai->driver->cops->pointer(cstream, tstamp, dai);
 784
 785        return soc_dai_ret(dai, ret);
 786}
 787EXPORT_SYMBOL_GPL(snd_soc_dai_compr_pointer);
 788
 789int snd_soc_dai_compr_set_metadata(struct snd_soc_dai *dai,
 790                                   struct snd_compr_stream *cstream,
 791                                   struct snd_compr_metadata *metadata)
 792{
 793        int ret = 0;
 794
 795        if (dai->driver->cops &&
 796            dai->driver->cops->set_metadata)
 797                ret = dai->driver->cops->set_metadata(cstream, metadata, dai);
 798
 799        return soc_dai_ret(dai, ret);
 800}
 801EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_metadata);
 802
 803int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai,
 804                                   struct snd_compr_stream *cstream,
 805                                   struct snd_compr_metadata *metadata)
 806{
 807        int ret = 0;
 808
 809        if (dai->driver->cops &&
 810            dai->driver->cops->get_metadata)
 811                ret = dai->driver->cops->get_metadata(cstream, metadata, dai);
 812
 813        return soc_dai_ret(dai, ret);
 814}
 815EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata);
 816