linux/sound/soc/soc-compress.c
<<
>>
Prefs
   1/*
   2 * soc-compress.c  --  ALSA SoC Compress
   3 *
   4 * Copyright (C) 2012 Intel Corp.
   5 *
   6 * Authors: Namarta Kohli <namartax.kohli@intel.com>
   7 *          Ramesh Babu K V <ramesh.babu@linux.intel.com>
   8 *          Vinod Koul <vinod.koul@linux.intel.com>
   9 *
  10 *  This program is free software; you can redistribute  it and/or modify it
  11 *  under  the terms of  the GNU General  Public License as published by the
  12 *  Free Software Foundation;  either version 2 of the  License, or (at your
  13 *  option) any later version.
  14 *
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/init.h>
  19#include <linux/delay.h>
  20#include <linux/slab.h>
  21#include <linux/workqueue.h>
  22#include <sound/core.h>
  23#include <sound/compress_params.h>
  24#include <sound/compress_driver.h>
  25#include <sound/soc.h>
  26#include <sound/initval.h>
  27
  28static int soc_compr_open(struct snd_compr_stream *cstream)
  29{
  30        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
  31        struct snd_soc_platform *platform = rtd->platform;
  32        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  33        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  34        int ret = 0;
  35
  36        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
  37
  38        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
  39                ret = platform->driver->compr_ops->open(cstream);
  40                if (ret < 0) {
  41                        pr_err("compress asoc: can't open platform %s\n", platform->name);
  42                        goto out;
  43                }
  44        }
  45
  46        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
  47                ret = rtd->dai_link->compr_ops->startup(cstream);
  48                if (ret < 0) {
  49                        pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name);
  50                        goto machine_err;
  51                }
  52        }
  53
  54        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  55                cpu_dai->playback_active++;
  56                codec_dai->playback_active++;
  57        } else {
  58                cpu_dai->capture_active++;
  59                codec_dai->capture_active++;
  60        }
  61
  62        cpu_dai->active++;
  63        codec_dai->active++;
  64        rtd->codec->active++;
  65
  66        mutex_unlock(&rtd->pcm_mutex);
  67
  68        return 0;
  69
  70machine_err:
  71        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
  72                platform->driver->compr_ops->free(cstream);
  73out:
  74        mutex_unlock(&rtd->pcm_mutex);
  75        return ret;
  76}
  77
  78/*
  79 * Power down the audio subsystem pmdown_time msecs after close is called.
  80 * This is to ensure there are no pops or clicks in between any music tracks
  81 * due to DAPM power cycling.
  82 */
  83static void close_delayed_work(struct work_struct *work)
  84{
  85        struct snd_soc_pcm_runtime *rtd =
  86                        container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
  87        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  88
  89        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
  90
  91        dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
  92                 codec_dai->driver->playback.stream_name,
  93                 codec_dai->playback_active ? "active" : "inactive",
  94                 rtd->pop_wait ? "yes" : "no");
  95
  96        /* are we waiting on this codec DAI stream */
  97        if (rtd->pop_wait == 1) {
  98                rtd->pop_wait = 0;
  99                snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
 100                                          SND_SOC_DAPM_STREAM_STOP);
 101        }
 102
 103        mutex_unlock(&rtd->pcm_mutex);
 104}
 105
 106static int soc_compr_free(struct snd_compr_stream *cstream)
 107{
 108        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 109        struct snd_soc_platform *platform = rtd->platform;
 110        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 111        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 112        struct snd_soc_codec *codec = rtd->codec;
 113
 114        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 115
 116        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
 117                cpu_dai->playback_active--;
 118                codec_dai->playback_active--;
 119        } else {
 120                cpu_dai->capture_active--;
 121                codec_dai->capture_active--;
 122        }
 123
 124        snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
 125
 126        cpu_dai->active--;
 127        codec_dai->active--;
 128        codec->active--;
 129
 130        if (!cpu_dai->active)
 131                cpu_dai->rate = 0;
 132
 133        if (!codec_dai->active)
 134                codec_dai->rate = 0;
 135
 136
 137        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
 138                rtd->dai_link->compr_ops->shutdown(cstream);
 139
 140        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
 141                platform->driver->compr_ops->free(cstream);
 142        cpu_dai->runtime = NULL;
 143
 144        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
 145                if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
 146                    rtd->dai_link->ignore_pmdown_time) {
 147                        snd_soc_dapm_stream_event(rtd,
 148                                        SNDRV_PCM_STREAM_PLAYBACK,
 149                                        SND_SOC_DAPM_STREAM_STOP);
 150                } else {
 151                        rtd->pop_wait = 1;
 152                        schedule_delayed_work(&rtd->delayed_work,
 153                                msecs_to_jiffies(rtd->pmdown_time));
 154                }
 155        } else {
 156                /* capture streams can be powered down now */
 157                snd_soc_dapm_stream_event(rtd,
 158                        SNDRV_PCM_STREAM_CAPTURE,
 159                        SND_SOC_DAPM_STREAM_STOP);
 160        }
 161
 162        mutex_unlock(&rtd->pcm_mutex);
 163        return 0;
 164}
 165
 166static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 167{
 168
 169        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 170        struct snd_soc_platform *platform = rtd->platform;
 171        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 172        int ret = 0;
 173
 174        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 175
 176        if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
 177                ret = platform->driver->compr_ops->trigger(cstream, cmd);
 178                if (ret < 0)
 179                        goto out;
 180        }
 181
 182        switch (cmd) {
 183        case SNDRV_PCM_TRIGGER_START:
 184                snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
 185                break;
 186        case SNDRV_PCM_TRIGGER_STOP:
 187                snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
 188                break;
 189        }
 190
 191out:
 192        mutex_unlock(&rtd->pcm_mutex);
 193        return ret;
 194}
 195
 196static int soc_compr_set_params(struct snd_compr_stream *cstream,
 197                                        struct snd_compr_params *params)
 198{
 199        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 200        struct snd_soc_platform *platform = rtd->platform;
 201        int ret = 0;
 202
 203        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 204
 205        /* first we call set_params for the platform driver
 206         * this should configure the soc side
 207         * if the machine has compressed ops then we call that as well
 208         * expectation is that platform and machine will configure everything
 209         * for this compress path, like configuring pcm port for codec
 210         */
 211        if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
 212                ret = platform->driver->compr_ops->set_params(cstream, params);
 213                if (ret < 0)
 214                        goto err;
 215        }
 216
 217        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
 218                ret = rtd->dai_link->compr_ops->set_params(cstream);
 219                if (ret < 0)
 220                        goto err;
 221        }
 222
 223        if (cstream->direction == SND_COMPRESS_PLAYBACK)
 224                snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
 225                                        SND_SOC_DAPM_STREAM_START);
 226        else
 227                snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
 228                                        SND_SOC_DAPM_STREAM_START);
 229
 230        /* cancel any delayed stream shutdown that is pending */
 231        rtd->pop_wait = 0;
 232        mutex_unlock(&rtd->pcm_mutex);
 233
 234        cancel_delayed_work_sync(&rtd->delayed_work);
 235
 236        return ret;
 237
 238err:
 239        mutex_unlock(&rtd->pcm_mutex);
 240        return ret;
 241}
 242
 243static int soc_compr_get_params(struct snd_compr_stream *cstream,
 244                                        struct snd_codec *params)
 245{
 246        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 247        struct snd_soc_platform *platform = rtd->platform;
 248        int ret = 0;
 249
 250        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 251
 252        if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
 253                ret = platform->driver->compr_ops->get_params(cstream, params);
 254
 255        mutex_unlock(&rtd->pcm_mutex);
 256        return ret;
 257}
 258
 259static int soc_compr_get_caps(struct snd_compr_stream *cstream,
 260                                struct snd_compr_caps *caps)
 261{
 262        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 263        struct snd_soc_platform *platform = rtd->platform;
 264        int ret = 0;
 265
 266        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 267
 268        if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
 269                ret = platform->driver->compr_ops->get_caps(cstream, caps);
 270
 271        mutex_unlock(&rtd->pcm_mutex);
 272        return ret;
 273}
 274
 275static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
 276                                struct snd_compr_codec_caps *codec)
 277{
 278        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 279        struct snd_soc_platform *platform = rtd->platform;
 280        int ret = 0;
 281
 282        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 283
 284        if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
 285                ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
 286
 287        mutex_unlock(&rtd->pcm_mutex);
 288        return ret;
 289}
 290
 291static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
 292{
 293        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 294        struct snd_soc_platform *platform = rtd->platform;
 295        int ret = 0;
 296
 297        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 298
 299        if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
 300                ret = platform->driver->compr_ops->ack(cstream, bytes);
 301
 302        mutex_unlock(&rtd->pcm_mutex);
 303        return ret;
 304}
 305
 306static int soc_compr_pointer(struct snd_compr_stream *cstream,
 307                        struct snd_compr_tstamp *tstamp)
 308{
 309        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 310        struct snd_soc_platform *platform = rtd->platform;
 311
 312        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 313
 314        if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
 315                 platform->driver->compr_ops->pointer(cstream, tstamp);
 316
 317        mutex_unlock(&rtd->pcm_mutex);
 318        return 0;
 319}
 320
 321static int soc_compr_copy(struct snd_compr_stream *cstream,
 322                          char __user *buf, size_t count)
 323{
 324        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 325        struct snd_soc_platform *platform = rtd->platform;
 326        int ret = 0;
 327
 328        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 329
 330        if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
 331                ret = platform->driver->compr_ops->copy(cstream, buf, count);
 332
 333        mutex_unlock(&rtd->pcm_mutex);
 334        return ret;
 335}
 336
 337static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
 338                                struct snd_compr_metadata *metadata)
 339{
 340        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 341        struct snd_soc_platform *platform = rtd->platform;
 342        int ret = 0;
 343
 344        if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata)
 345                ret = platform->driver->compr_ops->set_metadata(cstream, metadata);
 346
 347        return ret;
 348}
 349
 350static int sst_compr_get_metadata(struct snd_compr_stream *cstream,
 351                                struct snd_compr_metadata *metadata)
 352{
 353        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 354        struct snd_soc_platform *platform = rtd->platform;
 355        int ret = 0;
 356
 357        if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata)
 358                ret = platform->driver->compr_ops->get_metadata(cstream, metadata);
 359
 360        return ret;
 361}
 362/* ASoC Compress operations */
 363static struct snd_compr_ops soc_compr_ops = {
 364        .open           = soc_compr_open,
 365        .free           = soc_compr_free,
 366        .set_params     = soc_compr_set_params,
 367        .set_metadata   = sst_compr_set_metadata,
 368        .get_metadata   = sst_compr_get_metadata,
 369        .get_params     = soc_compr_get_params,
 370        .trigger        = soc_compr_trigger,
 371        .pointer        = soc_compr_pointer,
 372        .ack            = soc_compr_ack,
 373        .get_caps       = soc_compr_get_caps,
 374        .get_codec_caps = soc_compr_get_codec_caps
 375};
 376
 377/* create a new compress */
 378int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
 379{
 380        struct snd_soc_codec *codec = rtd->codec;
 381        struct snd_soc_platform *platform = rtd->platform;
 382        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 383        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 384        struct snd_compr *compr;
 385        char new_name[64];
 386        int ret = 0, direction = 0;
 387
 388        /* check client and interface hw capabilities */
 389        snprintf(new_name, sizeof(new_name), "%s %s-%d",
 390                        rtd->dai_link->stream_name, codec_dai->name, num);
 391
 392        if (codec_dai->driver->playback.channels_min)
 393                direction = SND_COMPRESS_PLAYBACK;
 394        else if (codec_dai->driver->capture.channels_min)
 395                direction = SND_COMPRESS_CAPTURE;
 396        else
 397                return -EINVAL;
 398
 399        compr = kzalloc(sizeof(*compr), GFP_KERNEL);
 400        if (compr == NULL) {
 401                snd_printk(KERN_ERR "Cannot allocate compr\n");
 402                return -ENOMEM;
 403        }
 404
 405        compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops),
 406                                  GFP_KERNEL);
 407        if (compr->ops == NULL) {
 408                dev_err(rtd->card->dev, "Cannot allocate compressed ops\n");
 409                ret = -ENOMEM;
 410                goto compr_err;
 411        }
 412        memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
 413
 414        /* Add copy callback for not memory mapped DSPs */
 415        if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
 416                compr->ops->copy = soc_compr_copy;
 417
 418        mutex_init(&compr->lock);
 419        ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
 420        if (ret < 0) {
 421                pr_err("compress asoc: can't create compress for codec %s\n",
 422                        codec->name);
 423                goto compr_err;
 424        }
 425
 426        /* DAPM dai link stream work */
 427        INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
 428
 429        rtd->compr = compr;
 430        compr->private_data = rtd;
 431
 432        printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
 433                cpu_dai->name);
 434        return ret;
 435
 436compr_err:
 437        kfree(compr);
 438        return ret;
 439}
 440